stride3d/stride · error · InvalidOperationException

Execution mode primitive is missing for first parameter of g

Error message

Execution mode primitive is missing for first parameter of geometry shader

What it means

When wrapping a geometry-shader entry point, GenerateWrapper reads the input primitive type from the modifiers of the first parameter (point/line/triangle/adjacency forms) so it can emit the matching SPIR-V OpExecutionMode (InputPoints, InputLines, Triangles, ...). If that parameter has ParameterModifiers.None, there is no primitive topology to encode, and the generator throws this InvalidOperationException. In SDSL/HLSL convention the first GS parameter must carry the input-primitive modifier.

Solutions

  1. Add the input primitive modifier to the first geometry shader parameter: point, line, triangle, lineadj, or triangleadj
  2. Verify the SDSL source actually parses the modifier (check for typos like 'triangles' vs the recognized forms)
  3. Confirm the shader is genuinely a geometry shader — for other stages remove the GS-style parameter so this path is not taken

Example fix

// before
void GSMain(StreamsInputStream inputs)
// after
void GSMain(triangle StreamsInputStream inputs)
Defensive patterns

Strategy: validation

Validate before calling

// Caller-side check before generating the wrapper
bool hasPrimitiveModifier = entryPointFunctionType.ParameterTypes[0].Modifiers != ParameterModifiers.None;

Type guard

static bool HasInputPrimitive(ParameterType p) => p.Modifiers is
    ParameterModifiers.Point or ParameterModifiers.Line or ParameterModifiers.LineAdjacency
    or ParameterModifiers.Triangle or ParameterModifiers.TriangleAdjacency;

Try / catch

try { wrapperGen.GenerateWrapper(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Execution mode primitive is missing"))
{
    // report: GS entry point missing input primitive modifier on first parameter
}

Prevention

When it happens

Trigger: Declaring a geometry shader entry point whose first parameter (the input patch/stream parameter) is written without a primitive modifier (e.g. missing `triangle`, `point`, `lineadj`, etc.) so its ParameterModifiers is None when the wrapper generator runs.

Common situations: Hand-porting an HLSL geometry shader where the primitive-type modifier was dropped or renamed; copying a vertex/fragment-style signature into a GS; SDSL parse layers that silently strip unknown modifiers so the generator sees None.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/7fe48b9796762b77. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Generation/EntryPointWrapperGenerator.cs:354

                    buffer.Add(new OpFunctionCall(voidType, context.Bound++, patchConstantEntryPoint.IdRef, new(patchArguments)));
                    ProcessTessellationArguments(patchConstantEntryPoint, patchArguments);

                    buffer.Add(new OpBranch(mergeLabel));

                    // 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}"),
                }, []));

View on GitHub (pinned to 96fad776d2)