stride3d/stride · error · NotSupportedException
Cannot simplify constant of type {value.GetType()}
Error message
Cannot simplify constant of type {value.GetType()} What it means
ShaderMixer's SPIR-V constant simplification path has dedicated handling for a fixed set of literal constant types (e.g. bool, int, long, float, double via BitConverter.DoubleToInt64Bits) and packs their bits into an OpData buffer. When the boxed runtime value passed in is of a type outside that switch, the default arm throws NotSupportedException naming the concrete CLR type. It is a compile-time guard: the mixer cannot emit a SPIR-V constant word layout for that type.
Solutions
- Find the value whose type is named in the message in the SDSL shader being compiled and change it to a supported literal type (int, float, double, bool, etc.).
- Cast the constant explicitly before it reaches the simplification path (e.g. (double)myValue) so it hits an existing case.
- If the type legitimately needs support, extend the switch in ShaderMixer.cs with a new case that packs the bits correctly (e.g. ulong via unchecked BitConverter path) and add a test.
Example fix
// before const uint SampleCount = 4; // boxed as uint -> default arm throws // after const int SampleCount = 4; // int case handles it
Defensive patterns
Strategy: type-guard
Validate before calling
static bool IsSupportedConstantType(object v) =>
v is bool or int or long or float or double; Type guard
if (value is not (bool or int or long or float or double))
throw new ArgumentException($"Unsupported constant type {value.GetType()}\", nameof(value)); Try / catch
try { SimplifyConstant(value); }
catch (NotSupportedException ex) { Log.Warn($"Skipping constant: {ex.Message}"); /* fall back to non-simplified path */ } Prevention
- Only pass primitive literal types (bool/int/long/float/double) into constant simplification.
- Cast enums and unsigned values to int/double before the mixer sees them.
- Add a unit test per supported constant type when extending the switch.
When it happens
Trigger: Calling the constant-simplification method in ShaderMixer.cs with a constant whose GetType() is not one of the explicitly cased literal types (e.g. char, uint, ulong, decimal, an enum, or a user struct) reaches the default: arm at ShaderMixer.cs:1191 and throws.
Common situations: SDSL source containing a constant of an exotic literal type that a mixer pass boxes; a refactor changed the value type (int -> uint or enum) feeding the constant path; a new numeric type was added upstream without extending the switch.
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 GetElementCount: {symbol}
- Unsupported type for GetElementType: {symbol}
- Unsupported type for WithElementType: {symbol}
- Unsupported constant type: {value.GetType()}
- Unsupported base type {p.BaseType} for indexer
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/eed1fb1aaab78222.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs:1191
{
case int v: mem.Span[3] = v; break;
case uint v: mem.Span[3] = unchecked((int)v); break;
case float v: mem.Span[3] = BitConverter.SingleToInt32Bits(v); break;
case long v:
mem.Span[3] = (int)(v & 0xFFFFFFFF);
mem.Span[4] = (int)(v >> 32);
break;
case ulong v:
mem.Span[3] = (int)(v & 0xFFFFFFFF);
mem.Span[4] = (int)(v >> 32);
break;
case double v:
var bits = BitConverter.DoubleToInt64Bits(v);
mem.Span[3] = (int)(bits & 0xFFFFFFFF);
mem.Span[4] = (int)(bits >> 32);
break;
default:
throw new NotSupportedException($"Cannot simplify constant of type {value.GetType()}");
}
buffer.Replace(index, new OpData(mem));
}
}
private static void RemoveInstructionWhere(SpirvBuffer buffer, Func<OpDataIndex, bool> match)
{
int insertIndex = 0;
for (int sourceIndex = 0; sourceIndex < buffer.Count; sourceIndex++)
{
var i = buffer[sourceIndex];
var remove = match(i);
if (!remove)
{
if (insertIndex++ != sourceIndex)
// Note: we're not using Dispose() since we simply move it
buffer.Replace(insertIndex - 1, buffer[sourceIndex].Data, false);View on GitHub (pinned to 96fad776d2)