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;