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
- 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
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
- 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
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
- Unsupported type for buffer layout
- Can't OpLoad with cbuffer
- Unsupported float width
- Unsupported type for storage buffer alignment
- Unsupported StreamsKindSDSL for output parameter
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 validatorView on GitHub (pinned to 96fad776d2)