gfx-rs/wgpu · error

Unexpected saturate({:?})

Error message

Unexpected saturate({:?})

What it means

Panic from the `unimplemented!()` arm handling `MathFunction::Saturate` in the SPIR-V backend. `saturate` is clamped between constants 0 and 1, which requires a float scalar (or float vector); if the argument type is any other TypeInner (e.g. integer scalar), the backend panics instead of producing a clean diagnostic.

Source

Thrown at naga/src/back/spv/block.rs:1453

                                max_id,
                                &[arg0_id, arg1_id],
                            ));

                            MathOp::Custom(Instruction::ext_inst_gl_op(
                                self.writer.gl450_ext_inst_id,
                                min_op,
                                result_type_id,
                                id,
                                &[max_id, arg2_id],
                            ))
                        }
                        other => unimplemented!("Unexpected max({:?})", other),
                    },
                    Mf::Saturate => {
                        let (maybe_size, scalar) = match *arg_ty {
                            crate::TypeInner::Vector { size, scalar } => (Some(size), scalar),
                            crate::TypeInner::Scalar(scalar) => (None, scalar),
                            ref other => unimplemented!("Unexpected saturate({:?})", other),
                        };
                        let scalar = crate::Scalar::float(scalar.width);
                        let mut arg1_id = self.writer.get_constant_scalar_with(0, scalar)?;
                        let mut arg2_id = self.writer.get_constant_scalar_with(1, scalar)?;

                        if let Some(size) = maybe_size {
                            let ty =
                                LocalType::Numeric(NumericType::Vector { size, scalar }).into();

                            self.temp_list.clear();
                            self.temp_list.resize(size as _, arg1_id);

                            arg1_id = self.writer.get_constant_composite(ty, &self.temp_list);

                            self.temp_list.fill(arg2_id);

                            arg2_id = self.writer.get_constant_composite(ty, &self.temp_list);
                        }

View on GitHub (pinned to 3e11ff59bf)

Solutions

  1. Explicitly convert the argument to a float type: `saturate(f32(x))`
  2. Verify the shader validates in the naga CLI before targeting SPIR-V to catch the type earlier
  3. Update to a newer wgpu/naga where this may be surfaced as a validation error
  4. Report a minimal reproducer to the wgpu repository if the shader looks valid

Example fix

// before
let s = saturate(i);
// after
let s = saturate(f32(i));
Defensive patterns

Strategy: validation

Validate before calling

fn check_saturate_operand(module: &naga::Module) -> bool {
    // saturate requires a float scalar or vector operand
    module.global_expressions.iter().all(|(_, e)| match e {
        naga::Expression::Math { fun: naga::MathFunction::Saturate, args, .. } => args.iter().all(|a| {
            matches!(ty_of(module, *a).inner,
                naga::TypeInner::Scalar(s) | naga::TypeInner::Vector { scalar: s, .. }
                if s.kind == naga::ScalarKind::Float)
        }),
        _ => true,
    })
}

Type guard

fn is_float_operand(t: &naga::TypeInner) -> bool {
    matches!(t, naga::TypeInner::Scalar(s) | naga::TypeInner::Vector { scalar: s, .. } if s.kind == naga::ScalarKind::Float)
}

Prevention

When it happens

Trigger: A `saturate(x)` call in a shader whose argument type is neither a float vector nor a float scalar, e.g. `saturate(some_i32)` reaching the SPIR-V writer via `cache_expression_value`.

Common situations: Generated or transpiled shaders (from GLSL/SPIR-V cross-compilation) where implicit conversions were expected but not present; WGSL authors usually get a validator error first, so this mostly affects non-WGSL frontends.

Related errors


AI-assisted analysis of gfx-rs/wgpu@3e11ff59bf (2026-09-03). Data as JSON: /api/errors/905ee5c0427316ab. Report an issue: GitHub.