stride3d/stride · error · NotImplementedException
Shader compositions can only be indexed with a constant
Error message
Shader compositions can only be indexed with a constant: '{indexer2.Index}' is resolved at mix time, not at runtime. What it means
Arrays of ShaderSymbol (shader compositions/mixin arrays) in SDSL are resolved at mix time, not at runtime, so they cannot be dynamically indexed by a runtime value. When the compiler encounters an IndexerExpression over a PointerType to an ArrayType of ShaderSymbol with an actual compiled index value, it throws this NotImplementedException. Only constant (compile-time) indexing of composition arrays is supported.
Solutions
- Use a compile-time constant index (literal or const value) when indexing shader composition arrays.
- Select the composition outside the shader via a branching composition/conditional mixin at effect-authoring time.
- Restructure so the runtime-selectable data lives in a regular (non-composition) array or a uniform buffer instead.
- Unroll the loop manually or use a static for-loop with constant bounds if a loop index is needed.
Example fix
// before (runtime index — not allowed) comps[i].Shade(result); // after (constant index / static selection) const int i = 1; comps[i].Shade(result);
Defensive patterns
Strategy: validation
Validate before calling
// composition arrays must be indexed by compile-time constants
if (arrayElemType is ShaderSymbol && !(indexExpr is LiteralExpression or { ValueType: ScalarType and ConstantValue != null }))
throw new InvalidOperationException("Shader composition arrays require constant indices"); Type guard
static bool IsConstantIndex(Expression e) => e is LiteralExpression || (e.ValueType is ScalarType s && s.HasConstantValue);
Try / catch
try { CompileShader(source); }
catch (NotImplementedException e) when (e.Message.Contains("resolved at mix time"))
{
// switch to a constant index or move selection to effect authoring
} Prevention
- Index composition arrays only with literals or const values.
- Move runtime variant selection to the effect/composition layer, not shader code.
- Keep runtime-selectable data in uniform buffers or regular arrays instead of composition arrays.
When it happens
Trigger: Writing compositionsArray[i] where i is a runtime/mix-time variable rather than a compile-time constant, inside shader code operating on an array of shader compositions.
Common situations: Trying to loop over composition arrays with a runtime index, selecting a mixin variant based on a runtime uniform, or porting code from regular arrays (runtime-indexable) to composition arrays (constant-index only).
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
- Could not find compositions for expression
- No composition was supplied for
- Composition variable
- Struct not found in shader
- External variable not found
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/8905383a6581be31.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs:1363
break;
// Array indexer for shader compositions: emit an OpAccessChain with a constant index,
// which ShaderMixer.ProcessMemberAccessAndForeach resolves to compositions[index] at
// mix time. A dynamic index has no composition to resolve to, hence the IntegerLiteral guard.
case (PointerType { BaseType: ArrayType { BaseType: ShaderSymbol compositionType } } p, IndexerExpression { Index: IntegerLiteral } indexer):
{
if (compiler == null)
{
indexer.Index.ProcessSymbol(table);
accessor.Type = new PointerType(compositionType, p.StorageClass);
break;
}
var indexerValue = indexer.Index.CompileAsValue(table, compiler);
PushAccessChainId(accessChainIds, indexerValue.Id);
break;
}
case (PointerType { BaseType: ArrayType { BaseType: ShaderSymbol } }, IndexerExpression indexer2):
throw new NotImplementedException(
$"Shader compositions can only be indexed with a constant: '{indexer2.Index}' is resolved at mix time, not at runtime.");
// Array indexer for arrays
case (PointerType { BaseType: ArrayType { BaseType: var t } } p, IndexerExpression indexer):
{
if (compiler == null)
{
indexer.Index.ProcessSymbol(table);
accessor.Type = new PointerType(t, p.StorageClass);
break;
}
var indexerValue = indexer.Index.CompileAsValue(table, compiler);
PushAccessChainId(accessChainIds, indexerValue.Id);
break;
}
// Array indexer for vector/matrix
case (PointerType { BaseType: VectorType or MatrixType } p, IndexerExpression indexer):
{
if (compiler == null)View on GitHub (pinned to 96fad776d2)