stride3d/stride · error · NotSupportedException
Unsupported element type for saturate
Error message
Unsupported element type for saturate: {functionType.ReturnType.GetElementType()} What it means
CompileSaturate implements saturate() as a clamp between 0 and 1, selecting the GLSL.std.450 FClamp/UClamp/SClamp opcode from the function's RETURN type element type. If the return type element is not Float, UInt or Int (e.g. double, bool), the opcode cannot be chosen and NotSupportedException is thrown.
Solutions
- Cast the value to float before saturate, e.g. saturate((float)x).
- Avoid saturate on double; write min(max(x, 0.0), 1.0) manually if doubles are truly needed.
- Check overload resolution so saturate receives a float/int/uint vector or scalar.
- If a scalar kind should be supported, add a case to the switch in CompileSaturate.
Example fix
// before (SDSL) double d = ...; float s = saturate(d); // throws // after float s = saturate((float)d);
Defensive patterns
Strategy: validation
Validate before calling
bool IsSaturatable(TypeBase returnType) =>
returnType.GetElementType() is ScalarType { Type: Scalar.Float or Scalar.UInt or Scalar.Int }; Type guard
bool IsFloatLike(TypeBase t) => t.GetElementType() is ScalarType { Type: Scalar.Float }; Try / catch
try { result = intrinsics.CompileSaturate(table, ctx, builder, fnType, x); }
catch (NotSupportedException ex) { /* suggest a cast to float */ } Prevention
- Avoid saturate on double; cast to float first
- Verify the expression's resolved return type, not the literal you wrote
- Prefer explicit min/max for non-standard element types
When it happens
Trigger: Calling saturate(x) where the expression's resolved return type has an unsupported element type — typically double (saturate of a double), or a bool/struct type from overload resolution gone wrong.
Common situations: Using saturate on double-precision math in a shader targeting an API without double clamp; a generic T function where T instantiates to an unexpected scalar; implicit conversions producing an unexpected return type.
Related errors
- Unsupported element type for clamp
- Unsupported mul operand types
- Unsupported element type for sign
- Unexpected type for f16tof32
- Unexpected type for f32tof16
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/9c72702676b53e60.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/IntrinsicImplementations.cs:241
return new(instruction.ResultId, instruction.ResultType);
}
public override SpirvValue CompileRound(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLRound, x);
public override SpirvValue CompileRsqrt(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLInverseSqrt, x);
public override SpirvValue CompileSqrt(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLSqrt, x);
public override SpirvValue CompileStep(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue x, TextLocation location = default) => CompileGLSLFloatBinaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLStep, a, x);
public override SpirvValue CompileSaturate(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)
{
// Ensure 0.0 amd 1.0 constants have same type as x
var constant0 = builder.Convert(context, context.CompileConstant(0.0f), functionType.ReturnType);
var constant1 = builder.Convert(context, context.CompileConstant(1.0f), functionType.ReturnType);
var instruction = functionType.ReturnType.GetElementType() switch
{
ScalarType { Type: Scalar.Float } => builder.InsertData(new GLSLFClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),
ScalarType { Type: Scalar.UInt } => builder.InsertData(new GLSLUClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),
ScalarType { Type: Scalar.Int } => builder.InsertData(new GLSLSClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),
_ => throw new NotSupportedException($"Unsupported element type for saturate: {functionType.ReturnType.GetElementType()}"),
};
return new(instruction);
}
public override SpirvValue CompileSign(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)
{
var sourceType = context.ReverseTypes[x.TypeId];
var instruction = sourceType.GetElementType() switch
{
ScalarType { Type: Scalar.Float or Scalar.Double } => builder.InsertData(new GLSLFSign(x.TypeId, context.Bound++, context.GetGLSL(), x.Id)),
ScalarType { Type: Scalar.UInt or Scalar.Int or Scalar.UInt64 or Scalar.Int64 } => builder.InsertData(new GLSLSSign(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id)),
_ => throw new NotSupportedException($"Unsupported element type for sign: {sourceType.GetElementType()}"),
};
// FSign return float whereas HLSL sign() expects int
return builder.Convert(context, new(instruction), functionType.ReturnType);
}
public override SpirvValue CompileSmoothstep(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, SpirvValue x, TextLocation location = default)
{View on GitHub (pinned to 96fad776d2)