stride3d/stride · error · NotSupportedException

Unsupported AlignmentRules value

Error message

Unsupported AlignmentRules value: {alignmentRules}

What it means

TypeSizeInBuffer computes the size/alignment of a type laid out in a constant or structured buffer. Array layout differs per AlignmentRules (CBuffer pads the stride to 16 bytes; StructuredBuffer packs elements); any other rule value reaches the default arm and throws NotSupportedException. Because it is recursive, an unknown rule propagates through every nested size computation.

Solutions

  1. Pass AlignmentRules.CBuffer or AlignmentRules.StructuredBuffer explicitly to the buffer-size API
  2. Add a case for the new AlignmentRules member in the Array/layout switch in Builder.CBuffer.cs:27
  3. Check the AlignmentRules value configured when creating the SPIR-V builder context

Example fix

// before
var rules = default(AlignmentRules);
var size = TypeSizeInBuffer(type, modifier, rules);
// after
var rules = AlignmentRules.StructuredBuffer;
var size = TypeSizeInBuffer(type, modifier, rules);
Defensive patterns

Strategy: validation

Validate before calling

bool IsDefined(AlignmentRules r) => r is AlignmentRules.CBuffer or AlignmentRules.StructuredBuffer;
if (!IsDefined(rules)) throw new InvalidEnumArgumentException(nameof(rules), (int)rules, typeof(AlignmentRules));

Type guard

static bool IsKnownAlignmentRules(AlignmentRules r) => r == AlignmentRules.CBuffer || r == AlignmentRules.StructuredBuffer;

Try / catch

try { size = TypeSizeInBuffer(type, modifier, rules); }
catch (NotSupportedException ex) { Log(ex.Message); size = TypeSizeInBuffer(type, modifier, AlignmentRules.StructuredBuffer); }

Prevention

When it happens

Trigger: Calling TypeSizeInBuffer (directly or via scalarSize/ComputeBufferOffset/elementSize/size) with an AlignmentRules value other than CBuffer or StructuredBuffer — e.g. a newly added enum member, a cast-in integer, or a default-initialized enum value not in the handled set.

Common situations: Adding a new AlignmentRules enum member (e.g. StorageBuffer/UniformBuffer variants) without updating the layout switches; passing a default(AlignmentRules) value when the enum's 0 is not a defined rule.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.CBuffer.cs:27

partial class SpirvBuilder
{
    public enum AlignmentRules
    {
        CBuffer,
        StructuredBuffer,
    }

    public static (int Size, int Alignment) TypeSizeInBuffer(SymbolType symbol, TypeModifier typeModifier, AlignmentRules alignmentRules)
    {
        // Helper to multiply size without changing alignment
        static (int Size, int Alignment) MultiplySize((int Size, int Alignment) current, int count) => (current.Size * count, current.Alignment);

        static (int Size, int Alignment) Array((int Size, int Alignment) current, int count, AlignmentRules alignmentRules) => alignmentRules switch
        {
            // HLSL array size: last element is not padded to stride
            AlignmentRules.CBuffer => ((current.Size + 15) / 16 * 16 * (count - 1) + current.Size, 16),
            AlignmentRules.StructuredBuffer => (current.Size * count, current.Alignment),
            _ => throw new NotSupportedException($"Unsupported AlignmentRules value: {alignmentRules}"),
        };
        return (symbol) switch
        {
            ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Float or Scalar.Boolean } => (4, 4),
            ScalarType { Type: Scalar.Int64 or Scalar.UInt64 or Scalar.Double } => (8, 8),
            StructuredType s => StructSizeInBuffer(s, alignmentRules),
            // StructuredBuffer uses std430 vector alignment (2×scalar for vec2, 4×scalar for vec3/vec4)
            // to satisfy Vulkan's relaxed block layout rule that a vector must not straddle a
            // 16-byte boundary. CBuffer keeps scalar alignment — ComputeBufferOffset handles the
            // "vector crossing 16-byte boundary" bump separately for that path.
            VectorType v when alignmentRules == AlignmentRules.StructuredBuffer
                => (TypeSizeInBuffer(v.BaseType, typeModifier, alignmentRules).Size * v.Size,
                    TypeSizeInBuffer(v.BaseType, typeModifier, alignmentRules).Alignment * (v.Size == 2 ? 2 : 4)),
            VectorType v => MultiplySize(TypeSizeInBuffer(v.BaseType, typeModifier, alignmentRules), v.Size),
            // Note: this is HLSL-style so Rows/Columns meaning is swapped
            // Note: HLSL default is ColumnMajor
            // StructuredBuffer uses std430 strict matrix layout: each column (ColumnMajor) or row
            // (RowMajor) is padded to its std430 base alignment, matching how the Vulkan validator

View on GitHub (pinned to 96fad776d2)