{"record":{"id":"905ee5c0427316ab","repo":"gfx-rs/wgpu","slug":"unexpected-saturate","errorCode":null,"errorMessage":"Unexpected saturate({:?})","messagePattern":"Unexpected saturate\\((.+?)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"naga/src/back/spv/block.rs","lineNumber":1453,"sourceCode":"                                max_id,\n                                &[arg0_id, arg1_id],\n                            ));\n\n                            MathOp::Custom(Instruction::ext_inst_gl_op(\n                                self.writer.gl450_ext_inst_id,\n                                min_op,\n                                result_type_id,\n                                id,\n                                &[max_id, arg2_id],\n                            ))\n                        }\n                        other => unimplemented!(\"Unexpected max({:?})\", other),\n                    },\n                    Mf::Saturate => {\n                        let (maybe_size, scalar) = match *arg_ty {\n                            crate::TypeInner::Vector { size, scalar } => (Some(size), scalar),\n                            crate::TypeInner::Scalar(scalar) => (None, scalar),\n                            ref other => unimplemented!(\"Unexpected saturate({:?})\", other),\n                        };\n                        let scalar = crate::Scalar::float(scalar.width);\n                        let mut arg1_id = self.writer.get_constant_scalar_with(0, scalar)?;\n                        let mut arg2_id = self.writer.get_constant_scalar_with(1, scalar)?;\n\n                        if let Some(size) = maybe_size {\n                            let ty =\n                                LocalType::Numeric(NumericType::Vector { size, scalar }).into();\n\n                            self.temp_list.clear();\n                            self.temp_list.resize(size as _, arg1_id);\n\n                            arg1_id = self.writer.get_constant_composite(ty, &self.temp_list);\n\n                            self.temp_list.fill(arg2_id);\n\n                            arg2_id = self.writer.get_constant_composite(ty, &self.temp_list);\n                        }","sourceCodeStart":1435,"sourceCodeEnd":1471,"githubUrl":"https://github.com/gfx-rs/wgpu/blob/3e11ff59bf3f9795d285ecc045014089640d7248/naga/src/back/spv/block.rs#L1435-L1471","documentation":"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.","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Explicitly convert the argument to a float type: `saturate(f32(x))`","Verify the shader validates in the naga CLI before targeting SPIR-V to catch the type earlier","Update to a newer wgpu/naga where this may be surfaced as a validation error","Report a minimal reproducer to the wgpu repository if the shader looks valid"],"exampleFix":"// before\nlet s = saturate(i);\n// after\nlet s = saturate(f32(i));","handlingStrategy":"validation","validationCode":"fn check_saturate_operand(module: &naga::Module) -> bool {\n    // saturate requires a float scalar or vector operand\n    module.global_expressions.iter().all(|(_, e)| match e {\n        naga::Expression::Math { fun: naga::MathFunction::Saturate, args, .. } => args.iter().all(|a| {\n            matches!(ty_of(module, *a).inner,\n                naga::TypeInner::Scalar(s) | naga::TypeInner::Vector { scalar: s, .. }\n                if s.kind == naga::ScalarKind::Float)\n        }),\n        _ => true,\n    })\n}","typeGuard":"fn is_float_operand(t: &naga::TypeInner) -> bool {\n    matches!(t, naga::TypeInner::Scalar(s) | naga::TypeInner::Vector { scalar: s, .. } if s.kind == naga::ScalarKind::Float)\n}","tryCatchPattern":null,"preventionTips":["Always call saturate on f32 values; insert f32() casts for ints","Run frontend validation before SPIR-V emission","Wrap shader compilation in catch_unwind for untrusted input","Reduce failing shaders with the naga CLI to find bad types"],"tags":["spirv","shader-compilation","naga","unimplemented"],"backgroundTag":"unimplemented-shader-feature","analyzedSha":"3e11ff59bf3f9795d285ecc045014089640d7248","analyzedAt":"2026-09-03T01:43:21.459Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}