stride3d/stride · error · NotSupportedException
Unsupported type for storage buffer alignment: {type}
Error message
Unsupported type for storage buffer alignment: {type} What it means
StorageBufferBaseAlignment computes std430-style base alignment for a type in a storage buffer: vectors use their element size, matrices map to a vector of their innermost dimension, arrays recurse on the element, structs take the max member alignment. A type outside these cases (e.g. an unresolved or unexpected symbol, or effectively a bare scalar at top level per this switch) throws NotSupportedException('Unsupported type for storage buffer alignment').
Solutions
- Ensure the type symbol fully resolves to Scalar/Vector/Matrix/Array/StructuredType before storage-buffer layout
- Implement a case for the unsupported type kind in Builder.CBuffer.cs:162 (e.g. treat bare scalars as (size, size))
- Log/inspect the type symbol to find why type resolution produced an unhandled kind
Example fix
// before
_ => throw new NotSupportedException($"Unsupported type for storage buffer alignment: {type}"),
// after
ScalarType sc => (SizeOf(sc), SizeOf(sc)),
_ => throw new NotSupportedException($"Unsupported type for storage buffer alignment: {type}") Defensive patterns
Strategy: type-guard
Validate before calling
bool HasStorageBufferAlignment(TypeSymbol t) => t is ScalarType or VectorType or MatrixType or ArrayType or StructuredType;
if (!HasStorageBufferAlignment(type)) throw new NotSupportedException($"Type {type} has no std430 alignment"); Type guard
static bool IsStd430Alignable(TypeSymbol t) => t is ScalarType or VectorType or MatrixType or ArrayType or StructuredType;
Try / catch
try { alignment = StorageBufferBaseAlignment(type, modifier); }
catch (NotSupportedException ex) { Log($"Unresolvable type for std430: {ex.Message}"); alignment = 16; /* conservative fallback */ } Prevention
- Resolve all type symbols fully before storage-buffer layout (no error/placeholder types)
- Extend the switch whenever new IR type kinds are added
- Run storage-buffer layout only on modules that passed type validation
- Test std430 alignment on shaders with nested arrays/structs/matrices
When it happens
Trigger: Calling StorageBufferBaseAlignment (directly or via vecAlignment/MaxMemberAlignment/alignment) with a symbol not matching Scalar/Vector/Matrix/Array/StructuredType — e.g. an error or placeholder type from failed resolution, or a new type node kind added to the IR.
Common situations: Storage-buffer layout of shaders with unresolved types after parse errors; new IR node types added without updating the alignment switch; deeply nested constructs where recursion reaches an unexpected leaf type.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Unsupported type for buffer layout: {symbol}
- Cannot simplify constant of type {value.GetType()}
- Unsupported constant type: {value.GetType()}
- Unsupported base type {p.BaseType} for indexer
- Unsupported composite type {Type} during regrouping
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/6b28ec3a7ef67449.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.CBuffer.cs:162
/// Computes the std430 base alignment of a type as required by Vulkan's storage buffer layout
/// (vec2 → 2×scalar, vec3/vec4 → 4×scalar, struct → max member alignment). Used to round the
/// ArrayStride of a [RW]StructuredBuffer element type so the SPIR-V validates under relaxed
/// block layout. Relaxed rules allow scalar-aligned offsets for vector members, but an array
/// of structs still needs its stride aligned to the struct's base alignment.
/// </summary>
public static int StorageBufferBaseAlignment(SymbolType type, TypeModifier typeModifier = TypeModifier.None) => type switch
{
ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Float or Scalar.Boolean or Scalar.Half } => 4,
ScalarType { Type: Scalar.Int64 or Scalar.UInt64 or Scalar.Double } => 8,
VectorType { Size: 2, BaseType: var bt } => 2 * StorageBufferBaseAlignment(bt),
VectorType { Size: 3 or 4, BaseType: var bt } => 4 * StorageBufferBaseAlignment(bt),
MatrixType m when typeModifier == TypeModifier.RowMajor
=> StorageBufferBaseAlignment(new VectorType(m.BaseType, m.Columns)),
MatrixType m
=> StorageBufferBaseAlignment(new VectorType(m.BaseType, m.Rows)),
ArrayType a => StorageBufferBaseAlignment(a.BaseType, typeModifier),
StructuredType s => MaxMemberAlignment(s),
_ => throw new NotSupportedException($"Unsupported type for storage buffer alignment: {type}"),
};
static int MaxMemberAlignment(StructuredType s)
{
var max = 4;
foreach (var member in s.Members)
max = Math.Max(max, StorageBufferBaseAlignment(member.Type, member.TypeModifier));
return max;
}
/// <summary>
/// Returns the ArrayStride required for <paramref name="elementType"/> when used as the element
/// of a [RW]StructuredBuffer's runtime array. The value is the packed size (via
/// <see cref="TypeSizeInBuffer"/>) rounded up to the type's std430 base alignment, so that the
/// emitted SPIR-V validates under relaxed block layout.
/// </summary>
public static int StorageBufferArrayStride(SymbolType elementType, TypeModifier typeModifier = TypeModifier.None)
{View on GitHub (pinned to 96fad776d2)