stride3d/stride · error · NotSupportedException

Unsupported conversion op

Error message

Unsupported conversion op: {op}

What it means

Within TryGetConstantValue's OpConvert* handling, only four numeric conversion opcodes (FToS, FToU, SToF, UToF) are implemented; any other conversion opcode reaching this switch throws NotSupportedException 'Unsupported conversion op'. Constant folding is limited to these scalar numeric conversions, so ops like OpConvertSToU, OpConvertUToS, or OpFConvert are unimplemented at fold time.

Solutions

  1. Avoid the conversion on constant operands in your shader-generation pipeline (emit the already-converted literal).
  2. Extend the switch with the missing OpConvert* arms (e.g. OpConvertSToU => (object)(uint)(int)convertOperand).
  3. Work around by emitting the instruction unconverted so it is folded downstream, or bypass constant folding for this expression.

Example fix

// before
Specification.Op.OpConvertUToF => (float)(uint)convertOperand,
_ => throw new NotSupportedException(...)
// after
Specification.Op.OpConvertUToF => (float)(uint)convertOperand,
Specification.Op.OpConvertSToU => (object)(uint)(int)convertOperand,
Specification.Op.OpConvertUToS => (object)(int)(uint)convertOperand,
Specification.Op.OpFConvert => (object)(double)(float)convertOperand,
_ => throw new NotSupportedException(...)
Defensive patterns

Strategy: validation

Validate before calling

static readonly Specification.Op[] FoldableConversions = { Specification.Op.OpConvertFToS, Specification.Op.OpConvertFToU, Specification.Op.OpConvertSToF, Specification.Op.OpConvertUToF };
if (!FoldableConversions.Contains(op)) EmitRuntimeInstruction(op); // skip folding

Type guard

bool IsFoldableConversion(Specification.Op op) => op is Specification.Op.OpConvertFToS or Specification.Op.OpConvertFToU or Specification.Op.OpConvertSToF or Specification.Op.OpConvertUToF;

Try / catch

try { folded = context.GetConstantValue(convertId); } catch (NotSupportedException ex) when (ex.Message.StartsWith("Unsupported conversion op")) { EmitRuntimeInstruction(op); }

Prevention

When it happens

Trigger: Constant-folding a SPIR-V OpConvertSToU, OpConvertUToS, or OpFConvert (or any future conversion opcode) whose operand is itself a constant, hitting the default arm of the conversion switch.

Common situations: Shaders produced by compilers that emit integer-to-integer conversions (S<->U) or float width conversions (FConvert, float<->double) on constant expressions; widening/narrowing literals in generated shader code.

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


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/c3485aae8d711b31. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs:105

            var resultId = i.Data.Memory.Span[2];
            var op = (Specification.Op)i.Data.Memory.Span[3];
            switch (op)
            {
                // Conversions
                case Specification.Op.OpConvertFToS:
                case Specification.Op.OpConvertFToU:
                case Specification.Op.OpConvertSToF:
                case Specification.Op.OpConvertUToF:
                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var convertOperand, out var convertOperandTypeId))
                        return false;
                    value = op switch
                    {
                        // Note: first cast to object is important, otherwise int/float will be cast as float
                        Specification.Op.OpConvertFToS => (object)(int)(float)convertOperand,
                        Specification.Op.OpConvertFToU => (uint)(float)convertOperand,
                        Specification.Op.OpConvertSToF => (float)(int)convertOperand,
                        Specification.Op.OpConvertUToF => (float)(uint)convertOperand,
                        _ => throw new NotSupportedException($"Unsupported conversion op: {op}"),
                    };
                    break;
                // Bitcast: reinterpret the operand's bits as the target scalar type.
                case Specification.Op.OpBitcast:
                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var bitcastOperand, out _))
                        return false;
                    if (ReverseTypes[resultType] is not ScalarType { Type: var bitcastTargetScalar })
                        throw new NotSupportedException($"OpBitcast result type {ReverseTypes[resultType]} is not a scalar");
                    var bitcastBits = bitcastOperand switch
                    {
                        int v => (uint)v,
                        uint v => v,
                        float v => BitConverter.SingleToUInt32Bits(v),
                        _ => throw new NotSupportedException($"OpBitcast operand type {bitcastOperand.GetType()} is not supported"),
                    };
                    value = bitcastTargetScalar switch
                    {
                        Scalar.Int => (object)(int)bitcastBits,

View on GitHub (pinned to 96fad776d2)