{"record":{"id":"161a819f0c78cd2b","repo":"stride3d/stride","slug":"can-t-figure-array-output-size-for-tessellation-shader","errorCode":null,"errorMessage":"Can't figure array output size for tessellation shader","messagePattern":"Can't figure array output size for tessellation shader","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Generation/EntryPointWrapperGenerator.cs","lineNumber":160,"sourceCode":"                        inputFieldValues[inputIndex] = BuiltinProcessor.ConvertInterfaceVariable(buffer, context, stream.InterfaceType, stream.Info.Type, inputFieldValues[inputIndex]);\n                    }\n\n                    inputValues[arrayIndex] = buffer.Add(new OpCompositeConstruct(context.GetOrRegister(streamLayout.InputType), context.Bound++, [.. inputFieldValues])).ResultId;\n                }\n\n                var inputsData1 = buffer.Add(new OpCompositeConstruct(context.GetOrRegister(new ArrayType(streamLayout.InputType, streamLayout.ArrayInputSize.Value)), context.Bound++, [.. inputValues])).ResultId;\n                return inputsData1;\n            }\n\n            var inputsData = ConvertInputsArray();\n\n            buffer.Add(new OpStore(inputsVariable, inputsData, null, []));\n\n            var entryPointTypeId = context.GetOrRegister(entryPoint.Type);\n            if (executionModel == ExecutionModel.TessellationControl || executionModel == ExecutionModel.TessellationEvaluation)\n            {\n                var arraySize = executionModel == ExecutionModel.TessellationControl\n                    ? streamLayout.ArrayOutputSize ?? throw new InvalidOperationException(\"Can't figure array output size for tessellation shader\")\n                    : streamLayout.ArrayInputSize.Value;\n                bool hullTessellationOutputsGenerated = false;\n                int GenerateHullTessellationOutputs()\n                {\n                    if (hullTessellationOutputsGenerated)\n                        throw new InvalidOperationException(\"Hull OutputPatch can only be used in once place (constant patch)\");\n                    hullTessellationOutputsGenerated = true;\n                    var outputsVariable = buffer.Insert(variableInsertIndex++, new OpVariable(context.GetOrRegister(new PointerType(new ArrayType(streamLayout.OutputType, arraySize), Specification.StorageClass.Function)), context.Bound++, Specification.StorageClass.Function, null)).ResultId;\n                    context.AddName(outputsVariable, \"outputs\");\n\n                    for (int arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex)\n                    {\n                        for (var outputIndex = 0; outputIndex < streamLayout.OutputStreams.Count; outputIndex++)\n                        {\n                            var stream = streamLayout.OutputStreams[outputIndex];\n                            var outputsVariablePtr = buffer.Add(new OpAccessChain(context.GetOrRegister(new PointerType(stream.Info.Type, Specification.StorageClass.Function)),\n                                context.Bound++, outputsVariable,\n                                [context.CompileConstant(arrayIndex).Id, context.CompileConstant(outputIndex).Id])).ResultId;","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Generation/EntryPointWrapperGenerator.cs#L142-L178","documentation":"For tessellation shaders, the SPIR-V entry-point wrapper needs the output (TessellationControl) or input (TessellationEvaluation) array size to declare arrayed function/Output variables. The stream layout's ArrayOutputSize (hull shader patch output count) was not provided, so the generator cannot compute the array length and throws InvalidOperationException.","triggerScenarios":"GenerateWrapper runs with executionModel == TessellationControl and streamLayout.ArrayOutputSize is null — i.e. the interface/streams layout for a hull shader was generated without an explicit patch output count.","commonSituations":"Hull shaders whose output patch count is not inferable from the SDSL/HLSL source (e.g. declared via custom streams or OutputPatch without a constant size); pipeline definitions built programmatically where ArrayOutputSize was left unset; older shader caches produced before array sizes were tracked.","solutions":["Set streamLayout.ArrayOutputSize explicitly (the patch constant count, e.g. [outputcontrolpoints(N)] equivalent) for the tessellation-control entry point.","Ensure the hull shader declares a constant OutputPatch/array size in the source so the layout generator can populate ArrayOutputSize.","Verify the layout construction code that builds streamLayout for tessellation pipelines and populate ArrayInputSize/ArrayOutputSize there.","Clear shader caches / recompile shaders after fixing the declaration, so stale layouts without the size are not reused."],"exampleFix":"// before (programmatic layout)\nvar streamLayout = new StreamLayout { OutputType = outputStruct }; // ArrayOutputSize left null\n\n// after\nvar streamLayout = new StreamLayout { OutputType = outputStruct, ArrayOutputSize = 4 }; // matches outputcontrolpoints(4)","handlingStrategy":"validation","validationCode":"// Before invoking GenerateWrapper for tessellation-control stages\nif (executionModel == ExecutionModel.TessellationControl && streamLayout.ArrayOutputSize is null)\n    throw new InvalidOperationException(\"Hull shader layout requires ArrayOutputSize (patch output control point count)\");\nif (executionModel == ExecutionModel.TessellationEvaluation && streamLayout.ArrayInputSize is null)\n    throw new InvalidOperationException(\"Domain shader layout requires ArrayInputSize\");","typeGuard":"static bool HasTessellationArraySizes(ExecutionModel model, StreamLayout l) =>\n    model == ExecutionModel.TessellationControl ? l.ArrayOutputSize.HasValue\n    : model == ExecutionModel.TessellationEvaluation ? l.ArrayInputSize.HasValue\n    : true;","tryCatchPattern":"try\n{\n    var wrapper = EntryPointWrapperGenerator.GenerateWrapper(...);\n}\ncatch (InvalidOperationException ex) when (ex.Message == \"Can't figure array output size for tessellation shader\")\n{\n    log.Error(\"Stream layout missing tessellation array size; regenerate layout with explicit patch counts\");\n    throw new ShaderCompilationException(\"Tessellation layout lacks ArrayOutputSize/ArrayInputSize\", ex);\n}","preventionTips":["Always declare constant output-control-point counts in hull shaders ([outputcontrolpoints(N)] equivalent) so sizes are inferable.","Populate ArrayOutputSize/ArrayInputSize in your layout-builder rather than relying on defaults.","Validate tessellation stream layouts in a pre-generation pass over all pipeline state.","Invalidate shader caches after changing tessellation declarations so stale layouts are not reused."],"tags":["spirv","tessellation","shader-compilation","missing-value"],"backgroundTag":"missing-required-config-field","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}