stride3d/stride · error · InvalidOperationException
Second parameter of geometry shader must be a GeometryStream
Error message
Second parameter of geometry shader must be a GeometryStreamType
What it means
For geometry shaders the wrapper generator expects the second entry-point parameter to be a pointer to a GeometryStreamType, from which it extracts the output topology before removing the parameter from the signature. If the cast `... as GeometryStreamType` fails (the parameter's pointee is some other type), this InvalidOperationException is thrown. It is a signature-shape contract: GS entry points must be (primitive-modified input stream, GeometryStreamType output).
Solutions
- Ensure the second parameter of the geometry entry point is the geometry stream output type (GeometryStreamType) — restore the conventional (input stream, output stream) ordering
- Check for parameter reordering in the SDSL source or a preprocessor pass that mutates the signature
- If your GS legitimately has a different signature, fix or extend EntryPointWrapperGenerator.GenerateWrapper to locate the GeometryStreamType parameter instead of hardcoding index 1
Example fix
// before (wrong second parameter type) void GSMain(line StreamsInputStream inputs, OutputPatch<VertexOutput, 3> patch) // after void GSMain(line StreamsInputStream inputs, TriangleStream<GSOutput> outputStream)
Defensive patterns
Strategy: validation
Validate before calling
bool secondIsGeometryStream = ((PointerType)entryPointFunctionType.ParameterTypes[1].Type).BaseType is GeometryStreamType;
Type guard
static bool IsGeometryStreamParameter(ParameterType p) =>
((PointerType)p.Type).BaseType is GeometryStreamType; Try / catch
try { wrapperGen.GenerateWrapper(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("must be a GeometryStreamType"))
{
// report: GS second parameter is not a GeometryStreamType; check signature ordering
} Prevention
- Keep the conventional GS signature order: (primitive-modified input stream, geometry stream output)
- Don't reorder GS parameters; the generator hardcodes parameter index 1
- Validate with IsGeometryStreamParameter before running wrapper generation
When it happens
Trigger: Declaring a geometry shader entry point whose second parameter is not a GeometryStreamType — e.g. a plain streams struct, an OutputPatch, a scalar, or parameters reordered so the stream-type parameter is no longer at index 1. Note the generator blindly assumes index 1 (see the 'TODO: Check it's really the 2nd parameter' comment).
Common situations: Reordering GS parameters so the input stream comes second; writing a GS-like function in a stage where stream types differ; an SDSL type-resolution regression where GeometryStreamType resolution yields the base struct instead of the stream type.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Semantic {semantic} requested with type {type} but last time
- Execution mode primitive is missing for first parameter of g
- Unsupported geometry input execution mode: {executionMode}
- Unsupported geometry stream output kind: {outputStreamType.K
- spirv_to_dxil_pipeline failed; SPIR-V dumped to {dumpPath} {
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/7967b7d7331da0b8.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Generation/EntryPointWrapperGenerator.cs:358
// Merge block
buffer.Add(new OpLabel(mergeLabel));
}
}
else if (executionModel == ExecutionModel.Geometry)
{
// Change signature of main() to not use the output Stream anymore
// TODO: Check it's really the 2nd parameter
SpirvBuilder.FunctionRemoveParameter(context, buffer, entryPoint, 1);
// Extract and remove execution mode (line, point, triangleadj, etc.)
var executionMode = entryPointFunctionType.ParameterTypes[0].Modifiers;
if (executionMode == ParameterModifiers.None)
throw new InvalidOperationException("Execution mode primitive is missing for first parameter of geometry shader");
// Extract output topology from the GeometryStreamType parameter before removing it
var outputStreamType = ((PointerType)entryPointFunctionType.ParameterTypes[1].Type).BaseType as GeometryStreamType
?? throw new InvalidOperationException("Second parameter of geometry shader must be a GeometryStreamType");
entryPointFunctionType.ParameterTypes[0] = entryPointFunctionType.ParameterTypes[0] with { Modifiers = ParameterModifiers.None };
entryPointFunctionType.ParameterTypes.RemoveAt(1);
context.ReplaceType(entryPointFunctionType, entryPointTypeId);
context.Add(new OpExecutionMode(entryPoint.IdRef, executionMode switch
{
ParameterModifiers.Point => ExecutionMode.InputPoints,
ParameterModifiers.Line => ExecutionMode.InputLines,
ParameterModifiers.LineAdjacency => ExecutionMode.InputLinesAdjacency,
ParameterModifiers.Triangle => ExecutionMode.Triangles,
ParameterModifiers.TriangleAdjacency => ExecutionMode.InputTrianglesAdjacency,
_ => throw new NotSupportedException($"Unsupported geometry input execution mode: {executionMode}"),
}, []));
context.Add(new OpExecutionMode(entryPoint.IdRef, outputStreamType.Kind switch
{
GeometryStreamOutputKindSDSL.Point => ExecutionMode.OutputPoints,
GeometryStreamOutputKindSDSL.Line => ExecutionMode.OutputLineStrip,View on GitHub (pinned to 96fad776d2)