stride3d/stride · error · NotSupportedException
Unsupported type cast
Error message
Unsupported type cast: {from} to {to} What it means
Thrown by Builder.Expressions.Convert in the per-element cast switch when the element type pair is a scalar combination not covered by the implemented SPIR-V conversions (OpConvertFToS/U, OpConvertSToF/U, OpUToF, OpSConvert, OpUConvert, OpBitcast int<->uint). Any other scalar pair, e.g. bool to float, double to bool, raises NotSupportedException.
Solutions
- Rewrite the cast in the shader using explicit constructs (e.g. `cond ? 1.0 : 0.0` instead of `(float)cond`).
- Extend the switch in Builder.Expressions.cs to emit the missing SPIR-V conversion (e.g. OpSelect for bool->numeric).
- Cast through an intermediate type that is supported (e.g. bool->int->float via logic ops).
Example fix
// before (shader) float f = (float)b; // after float f = b ? 1.0 : 0.0;
Defensive patterns
Strategy: type-guard
Validate before calling
static readonly HashSet<(Scalar,Scalar)> supported = new() { (Float,Int),(Float,UInt),(Int,Float),(UInt,Float),(Int,Int),(UInt,UInt),(Int,UInt),(UInt,Int) };
bool scalarCastSupported(Scalar from, Scalar to) => supported.Contains((from,to)) || from == to; Type guard
bool IsUnsupportedScalarCast(ScalarType a, ScalarType b) => a != b && !(a.Type is Scalar.Float or Scalar.Int or Scalar.UInt) || /* check pipeline table */ false;
Try / catch
try { result = builder.Convert(table, context, value, castType); }
catch (NotSupportedException e) when (e.Message.StartsWith("Unsupported type cast"))
{ /* rewrite the expression in shader source, e.g. bool ? 1 : 0 */ } Prevention
- Avoid casting bool to numeric types directly; use ternary/select expressions.
- Stick to float<->int/uint conversions supported by the SPIR-V converter.
- Consult the implemented conversion list in Builder.Expressions.cs before adding new casts.
When it happens
Trigger: Calling Convert where both element types are scalars but the (from, to) Scalar.Type pair has no emitted instruction — e.g. Float->Bool, Bool->Float, Double->Int on paths where only bitcast int<->uint was special-cased.
Common situations: Shader code casting to/from bool (common in conditionals like `(float)someBool`) or using half/double casts; the converter's supported numeric conversion matrix is narrower than HLSL.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Can't cast from to (larger matrix)
- Can't cast from to
- Can't cast from to
- Can't figure array output size for tessellation shader
- Can't OpLoad with cbuffer
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/557d9d4628dc9077.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:653
// Half conversions (OpFConvert for float<->half<->double, standard int conversions)
(ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Float }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Double }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Float }, ScalarType { Type: Scalar.Half }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Double }, ScalarType { Type: Scalar.Half }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Int }) => InsertData(new OpConvertFToS(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.UInt }) => InsertData(new OpConvertFToU(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Int }, ScalarType { Type: Scalar.Half }) => InsertData(new OpConvertSToF(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.UInt }, ScalarType { Type: Scalar.Half }) => InsertData(new OpConvertUToF(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Boolean }) => InsertData(new OpFOrdNotEqual(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue, context.CreateConstantCompositeVectorRepeat(new FloatLiteral(new(16, true, true), 0.0, new()), elementSize).Id)),
(ScalarType { Type: Scalar.Boolean }, ScalarType { Type: Scalar.Half }) => InsertData(new OpSelect(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue,
context.CreateConstantCompositeVectorRepeat(new FloatLiteral(new(16, true, true), 1.0, new()), elementSize).Id,
context.CreateConstantCompositeVectorRepeat(new FloatLiteral(new(16, true, true), 0.0, new()), elementSize).Id)),
// Bitcast (int=>uint or uint=>int)
(ScalarType { Type: Scalar.Int }, ScalarType { Type: Scalar.UInt }) => InsertData(new OpBitcast(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: Scalar.UInt }, ScalarType { Type: Scalar.Int }) => InsertData(new OpBitcast(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
(ScalarType { Type: var from }, ScalarType { Type: var to }) => throw new NotSupportedException($"Unsupported type cast: {from} to {to}"),
};
values[i] = typeCasting.IdResult!.Value;
// Update type
if (i == valueCount - 1)
valueType = castTypeSameSize;
}
}
// Expanding
int result = values[0];
switch (valueType, castType)
{
case (ScalarType, VectorType v2):
result = Insert(new OpCompositeConstruct(context.GetOrRegister(v2), context.Bound++, new LiteralArray<int>(Enumerable.Repeat(result, v2.Size).ToArray())));
valueType = v2;
break;
case (ScalarType, MatrixType m2):View on GitHub (pinned to 96fad776d2)