stride3d/stride · error · NotImplementedException

Cast from to is not implemented

Error message

Cast from {m1} to {v2} is not implemented (even though it should be valid since number of components is same

What it means

Thrown by Builder.Expressions.Convert when casting a matrix to a vector of the same total component count. The shape is valid, but the converter has no SPIR-V emission for this case, so it throws NotImplementedException noting it 'should be valid'.

Solutions

  1. Flatten manually in shader code (e.g. float4(m[0], m[1])).
  2. Implement the branch in Builder.Expressions.cs using OpCompositeExtract per column plus OpCompositeConstruct.
  3. Upgrade Stride to a version implementing this cast.

Example fix

// before
float4 v = (float4)m2x2;
// after
float4 v = float4(m2x2[0], m2x2[1]);
Defensive patterns

Strategy: fallback

Validate before calling

// flatten manually instead of casting
// float4 v = float4(m[0], m[1]);

Type guard

bool IsUnsupportedMatToVec(SymbolType from, SymbolType to) => from is MatrixType m && to is VectorType v && v.Size == m.Rows * m.Columns;

Try / catch

try { Compile(shader); }
catch (NotImplementedException e) { Log.Warn(e.Message); /* replace cast in shader source */ }

Prevention

When it happens

Trigger: Calling Convert with (MatrixType, VectorType) where v2.Size == m1.Rows * m1.Columns (e.g. 2x2 matrix -> vec4); reached through ConvertTexCoord, ConvertOffset, CompileSign, MultiplyConstant, etc.

Common situations: HLSL-style flattening casts such as `(float4)myMat2x2` in shader source; an upstream Stride limitation rather than caller error.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:576

                {
                    Span<int> valuesTemp = stackalloc int[v2.Size];
                    for (int i = 0; i < v2.Size; ++i)
                        Insert(new OpCompositeExtract(context.GetOrRegister(v1.BaseType), valuesTemp[i] = context.Bound++, valueId, [i]));
                    valueType = new VectorType(v1.BaseType, v2.Size);
                    values[0] = Insert(new OpCompositeConstruct(context.GetOrRegister(valueType), context.Bound++, new LiteralArray<int>(valuesTemp))).ResultId;
                    break;
                }
            case (VectorType v1, MatrixType m2) when v1.Size != m2.Rows * m2.Columns:
                throw new InvalidOperationException($"Can't cast from {v1} to {m2}");
            case (VectorType v1, MatrixType m2) when v1.Size == m2.Rows * m2.Columns:
                throw new NotImplementedException($"Cast from {v1} to {m2} is not implemented (even though it should be valid since number of components is same");
            case (MatrixType m1, ScalarType s2):
                values[0] = Insert(new OpCompositeExtract(context.GetOrRegister(m1.BaseType), context.Bound++, valueId, [0, 0])).ResultId;
                break;
            case (MatrixType m1, VectorType v2) when v2.Size != m1.Rows * m1.Columns:
                throw new InvalidOperationException($"Can't cast from {m1} to {v2}");
            case (MatrixType m1, VectorType v2) when v2.Size == m1.Rows * m1.Columns:
                throw new NotImplementedException($"Cast from {m1} to {v2} is not implemented (even though it should be valid since number of components is same");
            case (MatrixType m1, MatrixType m2) when m1.Rows < m2.Rows || m1.Columns < m2.Columns:
                throw new InvalidOperationException($"Can't cast from {m1} to {m2} (larger matrix)");
            case (MatrixType m1, MatrixType m2) when m1.Rows >= m2.Rows && m1.Columns >= m2.Columns:
                {
                    // Extract each of the m2.Columns leading matrix columns, and shorten it from
                    // m1.Rows down to m2.Rows components when the column vectors also shrink.
                    Span<int> shuffleIndices = stackalloc int[m2.Rows];
                    for (int j = 0; j < m2.Rows; ++j)
                        shuffleIndices[j] = j;
                    for (int i = 0; i < m2.Columns; ++i)
                    {
                        values[i] = Insert(new OpCompositeExtract(context.GetOrRegister(new VectorType(m1.BaseType, m1.Rows)), context.Bound++, valueId, [i])).ResultId;
                        if (m1.Rows != m2.Rows)
                        {
                            values[i] = Insert(new OpVectorShuffle(context.GetOrRegister(new VectorType(m1.BaseType, m2.Rows)), context.Bound++, values[i], values[i], new(shuffleIndices))).ResultId;
                        }
                    }
                    valueType = new VectorType(m1.BaseType, m2.Rows);

View on GitHub (pinned to 96fad776d2)