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

  1. Ensure the second parameter of the geometry entry point is the geometry stream output type (GeometryStreamType) — restore the conventional (input stream, output stream) ordering
  2. Check for parameter reordering in the SDSL source or a preprocessor pass that mutates the signature
  3. 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

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


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)