[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 <jcapucho7@gmail.com>

* Fix grammar.

Co-authored-by: JCapucho <jcapucho7@gmail.com>
This commit is contained in:
Shohei Jinno
2022-04-11 12:02:34 -07:00
committed by GitHub
parent 8f5d8f61a8
commit b21634ac61

View File

@@ -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;