From b21634ac613b8ff3714407f7e8a2c3b58ab32fef Mon Sep 17 00:00:00 2001 From: Shohei Jinno <18493164+sjinno@users.noreply.github.com> Date: Mon, 11 Apr 2022 12:02:34 -0700 Subject: [PATCH] [glsl-in] Implement `MathFunction::Clamp` (#1502) * Implement Clamp function. * Fix the return error type. Change `ConstantSolvingError::InvalidMathArg` to `ConstantSolvingError::NotImplemented` for the clamp function with the input of unimplemented types. Unimplemented types: - vec2 - vec3 - vec4 * [glsl-in] Add helpers that implement the glsl's `max` and `min` functions. Co-authored-by: JCapucho * Fix grammar. Co-authored-by: JCapucho --- src/front/glsl/constants.rs | 95 ++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src/front/glsl/constants.rs b/src/front/glsl/constants.rs index ea81123081..e537370858 100644 --- a/src/front/glsl/constants.rs +++ b/src/front/glsl/constants.rs @@ -164,12 +164,20 @@ impl<'a> ConstantSolver<'a> { self.binary_op(op, left_constant, right_constant, span) } - Expression::Math { fun, arg, arg1, .. } => { + Expression::Math { + fun, + arg, + arg1, + arg2, + .. + } => { let arg = self.solve(arg)?; let arg1 = arg1.map(|arg| self.solve(arg)).transpose()?; + let arg2 = arg2.map(|arg| self.solve(arg)).transpose()?; let const0 = &self.constants[arg].inner; let const1 = arg1.map(|arg| &self.constants[arg].inner); + let const2 = arg2.map(|arg| &self.constants[arg].inner); match fun { crate::MathFunction::Pow => { @@ -201,6 +209,49 @@ impl<'a> ConstantSolver<'a> { let inner = ConstantInner::Scalar { width, value }; Ok(self.register_constant(inner, span)) } + crate::MathFunction::Clamp => { + let (value, width) = match (const0, const1.unwrap(), const2.unwrap()) { + ( + &ConstantInner::Scalar { + width, + value: value0, + }, + &ConstantInner::Scalar { value: value1, .. }, + &ConstantInner::Scalar { value: value2, .. }, + ) => ( + match (value0, value1, value2) { + ( + ScalarValue::Sint(a), + ScalarValue::Sint(b), + ScalarValue::Sint(c), + ) => ScalarValue::Sint(a.max(b).min(c)), + ( + ScalarValue::Uint(a), + ScalarValue::Uint(b), + ScalarValue::Uint(c), + ) => ScalarValue::Uint(a.max(b).min(c)), + ( + ScalarValue::Float(a), + ScalarValue::Float(b), + ScalarValue::Float(c), + ) => { + ScalarValue::Float(glsl_float_min(glsl_float_max(a, b), c)) + } + _ => return Err(ConstantSolvingError::InvalidMathArg), + }, + width, + ), + _ => { + return Err(ConstantSolvingError::NotImplemented(format!( + "{:?} applied to vector values", + fun + ))) + } + }; + + let inner = ConstantInner::Scalar { width, value }; + Ok(self.register_constant(inner, span)) + } _ => Err(ConstantSolvingError::NotImplemented(format!("{:?}", fun))), } } @@ -506,6 +557,48 @@ impl<'a> ConstantSolver<'a> { } } +/// Helper function to implement the GLSL `max` function for floats. +/// +/// While Rust does provide a `f64::max` method, it has a different behavior than the +/// GLSL `max` for NaNs. In Rust, if any of the arguments is a NaN, then the other +/// is returned. +/// +/// This leads to different results in the following example +/// ``` +/// use std::cmp::max; +/// std::f64::NAN.max(1.0); +/// ``` +/// +/// Rust will return `1.0` while GLSL should return NaN. +fn glsl_float_max(x: f64, y: f64) -> f64 { + if x < y { + y + } else { + x + } +} + +/// Helper function to implement the GLSL `min` function for floats. +/// +/// While Rust does provide a `f64::min` method, it has a different behavior than the +/// GLSL `min` for NaNs. In Rust, if any of the arguments is a NaN, then the other +/// is returned. +/// +/// This leads to different results in the following example +/// ``` +/// use std::cmp::min; +/// std::f64::NAN.min(1.0); +/// ``` +/// +/// Rust will return `1.0` while GLSL should return NaN. +fn glsl_float_min(x: f64, y: f64) -> f64 { + if y < x { + y + } else { + x + } +} + #[cfg(test)] mod tests { use std::vec;