{"record":{"id":"6582ad0d3aa84190","repo":"stride3d/stride","slug":"unsupported-alignmentrules-value-alignmentrules","errorCode":null,"errorMessage":"Unsupported AlignmentRules value: {alignmentRules}","messagePattern":"Unsupported AlignmentRules value: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.CBuffer.cs","lineNumber":27,"sourceCode":"partial class SpirvBuilder\n{\n    public enum AlignmentRules\n    {\n        CBuffer,\n        StructuredBuffer,\n    }\n\n    public static (int Size, int Alignment) TypeSizeInBuffer(SymbolType symbol, TypeModifier typeModifier, AlignmentRules alignmentRules)\n    {\n        // Helper to multiply size without changing alignment\n        static (int Size, int Alignment) MultiplySize((int Size, int Alignment) current, int count) => (current.Size * count, current.Alignment);\n\n        static (int Size, int Alignment) Array((int Size, int Alignment) current, int count, AlignmentRules alignmentRules) => alignmentRules switch\n        {\n            // HLSL array size: last element is not padded to stride\n            AlignmentRules.CBuffer => ((current.Size + 15) / 16 * 16 * (count - 1) + current.Size, 16),\n            AlignmentRules.StructuredBuffer => (current.Size * count, current.Alignment),\n            _ => throw new NotSupportedException($\"Unsupported AlignmentRules value: {alignmentRules}\"),\n        };\n        return (symbol) switch\n        {\n            ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Float or Scalar.Boolean } => (4, 4),\n            ScalarType { Type: Scalar.Int64 or Scalar.UInt64 or Scalar.Double } => (8, 8),\n            StructuredType s => StructSizeInBuffer(s, alignmentRules),\n            // StructuredBuffer uses std430 vector alignment (2×scalar for vec2, 4×scalar for vec3/vec4)\n            // to satisfy Vulkan's relaxed block layout rule that a vector must not straddle a\n            // 16-byte boundary. CBuffer keeps scalar alignment — ComputeBufferOffset handles the\n            // \"vector crossing 16-byte boundary\" bump separately for that path.\n            VectorType v when alignmentRules == AlignmentRules.StructuredBuffer\n                => (TypeSizeInBuffer(v.BaseType, typeModifier, alignmentRules).Size * v.Size,\n                    TypeSizeInBuffer(v.BaseType, typeModifier, alignmentRules).Alignment * (v.Size == 2 ? 2 : 4)),\n            VectorType v => MultiplySize(TypeSizeInBuffer(v.BaseType, typeModifier, alignmentRules), v.Size),\n            // Note: this is HLSL-style so Rows/Columns meaning is swapped\n            // Note: HLSL default is ColumnMajor\n            // StructuredBuffer uses std430 strict matrix layout: each column (ColumnMajor) or row\n            // (RowMajor) is padded to its std430 base alignment, matching how the Vulkan validator","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.CBuffer.cs#L9-L45","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass AlignmentRules.CBuffer or AlignmentRules.StructuredBuffer explicitly to the buffer-size API","Add a case for the new AlignmentRules member in the Array/layout switch in Builder.CBuffer.cs:27","Check the AlignmentRules value configured when creating the SPIR-V builder context"],"exampleFix":"// before\nvar rules = default(AlignmentRules);\nvar size = TypeSizeInBuffer(type, modifier, rules);\n// after\nvar rules = AlignmentRules.StructuredBuffer;\nvar size = TypeSizeInBuffer(type, modifier, rules);","handlingStrategy":"validation","validationCode":"bool IsDefined(AlignmentRules r) => r is AlignmentRules.CBuffer or AlignmentRules.StructuredBuffer;\nif (!IsDefined(rules)) throw new InvalidEnumArgumentException(nameof(rules), (int)rules, typeof(AlignmentRules));","typeGuard":"static bool IsKnownAlignmentRules(AlignmentRules r) => r == AlignmentRules.CBuffer || r == AlignmentRules.StructuredBuffer;","tryCatchPattern":"try { size = TypeSizeInBuffer(type, modifier, rules); }\ncatch (NotSupportedException ex) { Log(ex.Message); size = TypeSizeInBuffer(type, modifier, AlignmentRules.StructuredBuffer); }","preventionTips":["Never pass default(AlignmentRules); always set an explicit rule","When extending the AlignmentRules enum, update every switch in Builder.CBuffer.cs","Assert the rule value at builder-construction time","Keep enum values defined starting at 0 so defaults are valid"],"tags":["spirv","layout","unsupported-value","cbuffer"],"backgroundTag":"unsupported-enum-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}