stride3d/stride · error · InvalidOperationException

Unexpected type for f16tof32

Error message

Unexpected type {inputType} for f16tof32

What it means

CompileF32tof16's sibling f16tof32 expects its input to resolve to a uint or vector-of-uint (packed half values) so it can unpack via GLSL UnpackHalf2x16, extract components, and recompose. If the input type is anything else, it throws InvalidOperationException with the resolved input type, since the packed-half unpacking path cannot apply.

Solutions

  1. Ensure the argument is uint or a uint vector holding packed half values (asuint on the packed data).
  2. Reinterpret with asuint(...) if the bits are currently typed as something else.
  3. If you intended a plain float conversion, use a cast instead of f16tof32.
  4. If a new input kind (e.g. scalar uint handled separately) should be supported, extend CompileF16tof32's type dispatch.

Example fix

// before (SDSL)
float x = 1.5;
float y = f16tof32(x); // throws: Unexpected type float
// after
uint packed = asuint(...);
float y = f16tof32(packed);
Defensive patterns

Strategy: validation

Validate before calling

bool IsPackedUint(TypeBase t) =>
    t is ScalarType { Type: Scalar.UInt } or VectorType { BaseType: ScalarType { Type: Scalar.UInt } };

Type guard

bool IsUintOrUintVec(TypeBase t) => t.GetElementType() is ScalarType { Type: Scalar.UInt };

Try / catch

try { result = intrinsics.CompileF16tof32(table, ctx, builder, fnType, x); }
catch (InvalidOperationException ex) { /* report that input must be uint of packed halves */ }

Prevention

When it happens

Trigger: Calling f16tof32(x) where x is float, int, half, or a vector whose element type is not uint — the intrinsic requires the packed 16-bit representation held in uint(s).

Common situations: Forgetting that HLSL f16tof32 takes a uint (or uint-vector) of packed halves and passing the original float/half value; assigning the result of an intermediate computation to a float variable and feeding it back.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/IntrinsicImplementations.cs:417

        else if (inputType is VectorType v)
        {
            var uintType = context.GetOrRegister(ScalarType.UInt);
            var components = new int[v.Size];
            for (int i = 0; i < v.Size; i++)
            {
                // Extract uint component
                var comp = new SpirvValue(builder.InsertData(new OpCompositeExtract(uintType, context.Bound++, x.Id, [i])));
                // UnpackHalf2x16 -> float2
                var unpack = builder.Insert(new GLSLExp(float2Type, context.Bound++, context.GetGLSL(), comp.Id));
                unpack.InstructionMemory.Span[4] = 62; // GLSLstd450 UnpackHalf2x16
                // Extract .x -> float
                var extract = new SpirvValue(builder.InsertData(new OpCompositeExtract(floatType, context.Bound++, unpack.ResultId, [0])));
                components[i] = extract.Id;
            }
            var result = new SpirvValue(builder.InsertData(new OpCompositeConstruct(context.GetOrRegister(returnType), context.Bound++, [.. components])));
            return result;
        }
        throw new InvalidOperationException($"Unexpected type {inputType} for f16tof32");
    }
    public override SpirvValue CompileF32tof16(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)
    {
        // f32tof16(float) -> uint: PackHalf2x16(float2(x, 0.0)) -> uint
        // For vector variants: decompose, apply per-element, recompose
        var returnType = functionType.ReturnType;
        var inputType = context.ReverseTypes[x.TypeId];
        var float2Type = context.GetOrRegister(new VectorType(ScalarType.Float, 2));
        var floatType = context.GetOrRegister(ScalarType.Float);
        var uintType = context.GetOrRegister(ScalarType.UInt);
        var zero = context.AddConstant(0.0f);

        if (inputType is ScalarType)
        {
            // Construct float2(x, 0.0)
            var float2Val = new SpirvValue(builder.InsertData(new OpCompositeConstruct(float2Type, context.Bound++, [x.Id, zero])));
            // PackHalf2x16(float2) -> uint
            var pack = builder.Insert(new GLSLExp(uintType, context.Bound++, context.GetGLSL(), float2Val.Id));

View on GitHub (pinned to 96fad776d2)