stride3d/stride · error · NotSupportedException
Unsupported element type for clamp
Error message
Unsupported element type for clamp: {context.ReverseTypes[x.TypeId].GetElementType()} What it means
CompileClamp lowers the SDSL clamp() intrinsic to a SPIR-V GLSL.std.450 clamp opcode (FClamp/UClamp/SClamp) chosen by the element type of the operand. When the operand's element type is not Float, UInt or Int (e.g. bool, double, 64-bit ints), no opcode variant exists and the compiler throws NotSupportedException, reporting the resolved element type for diagnosis.
Solutions
- Check the operand's element type; convert to float/int/uint (e.g. cast or use asfloat) before clamp.
- If clamping a double, cast to float first or implement manual min/max with supported types.
- If the operand is bool, restructure logic so clamp only receives numeric values.
- If you believe the element type should be supported, extend the switch in IntrinsicImplementations.CompileClamp to map the missing ScalarType to the appropriate GLSL clamp opcode.
Example fix
// before (SDSL) double x = ...; float c = clamp(x, 0.0, 1.0); // throws // after float c = clamp((float)x, 0.0f, 1.0f);
Defensive patterns
Strategy: validation
Validate before calling
bool IsClampable(SpirvContext ctx, SpirvValue v) =>
ctx.ReverseTypes[v.TypeId].GetElementType() is ScalarType { Type: Scalar.Float or Scalar.UInt or Scalar.Int }; Type guard
bool IsScalarOf(TypeBase t, Scalar k) => t.GetElementType() is ScalarType s && s.Type == k;
Try / catch
try { result = intrinsics.CompileClamp(table, ctx, builder, fnType, x, min, max); }
catch (NotSupportedException ex) { /* report shader source location & operand element type */ } Prevention
- Only call clamp on float/int/uint (vectors of) values
- Cast doubles and halves to float before clamping
- Check overload resolution when using generics in SDSL
When it happens
Trigger: Calling clamp() in SDSL/HLSL shader code whose argument resolves to a vector/matrix/scalar whose element type is not one of float, uint, int — e.g. clamp() on a bool vector, a double value, or an unexpected type from a generic/t intrinsic instantiation.
Common situations: Using clamp on a half/double typed value assumed to behave like float; passing a bool result of a comparison into clamp; a generic shader function where T instantiates to an unsupported scalar; typos producing a wrong overload resolution.
Related errors
- Unsupported mul operand types
- Unsupported element type for saturate
- 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/b9305b3b9ffebd01.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/IntrinsicImplementations.cs:137
{
var instruction = context.ReverseTypes[a.TypeId].GetElementType() switch
{
ScalarType { Type: Scalar.Float or Scalar.Double } => builder.InsertData(new GLSLFMax(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), a.Id, b.Id)),
ScalarType { Type: Scalar.UInt } => builder.InsertData(new GLSLUMax(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), a.Id, b.Id)),
ScalarType { Type: Scalar.Int } => builder.InsertData(new GLSLSMax(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), a.Id, b.Id)),
_ => throw new NotSupportedException($"Unsupported element type for max: {context.ReverseTypes[a.TypeId].GetElementType()}"),
};
return new(instruction);
}
public override SpirvValue CompileClamp(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, SpirvValue min, SpirvValue max, TextLocation location = default)
{
var instruction = context.ReverseTypes[x.TypeId].GetElementType() switch
{
ScalarType { Type: Scalar.Float } => builder.InsertData(new GLSLFClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, min.Id, max.Id)),
ScalarType { Type: Scalar.UInt } => builder.InsertData(new GLSLUClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, min.Id, max.Id)),
ScalarType { Type: Scalar.Int } => builder.InsertData(new GLSLSClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, min.Id, max.Id)),
_ => throw new NotSupportedException($"Unsupported element type for clamp: {context.ReverseTypes[x.TypeId].GetElementType()}"),
};
return new(instruction);
}
public override SpirvValue CompileRadians(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLRadians, x);
public override SpirvValue CompileDegrees(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLDegrees, x);
public override SpirvValue CompileExp(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLExp, x);
public override SpirvValue CompileExp2(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLExp2, x);
public override SpirvValue CompileLog(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLLog, x);
public override SpirvValue CompileLog10(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => MultiplyConstant(table, context, builder, functionType.ReturnType, CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLLog2, x), (float)Math.Log10(2.0));
public override SpirvValue CompileLog2(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLLog2, x);
public override SpirvValue CompilePow(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, SpirvValue y, TextLocation location = default) => CompileGLSLFloatBinaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLPow, x, y);
// Vector math
public override SpirvValue CompileDistance(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, TextLocation location = default)
{
var instruction = builder.Insert(new GLSLDistance(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), a.Id, b.Id));View on GitHub (pinned to 96fad776d2)