{"record":{"id":"cead8179912abe9c","repo":"stride3d/stride","slug":"interfaceprocessor-at-least-a-pixel-or-compute-shader-is","errorCode":null,"errorMessage":"InterfaceProcessor: At least a pixel or compute shader is expected","messagePattern":"InterfaceProcessor: At least a pixel or compute shader is expected","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/InterfaceProcessor.cs","lineNumber":150,"sourceCode":"\n            return chain;\n        }\n\n        public Result Process(SymbolTable table, SpirvBuffer buffer, SpirvContext context)\n        {\n            // OpEntryPoint emission is deferred to allow fixups (e.g. adding dummy DS inputs for HS-internal outputs)\n            var entryPoints = new List<EntryPointInfo>();\n\n            compositionFunctions = CollectCompositionFunctions(buffer);\n\n            var entryPointVS = ResolveEntryPoint(table, \"VSMain\");\n            var entryPointHS = ResolveEntryPoint(table, \"HSMain\");\n            var entryPointDS = ResolveEntryPoint(table, \"DSMain\");\n            var entryPointGS = ResolveEntryPoint(table, \"GSMain\");\n            var entryPointPS = ResolveEntryPoint(table, \"PSMain\");\n            var entryPointCS = ResolveEntryPoint(table, \"CSMain\");\n\n            var entryPointPSOrCS = entryPointCS ?? entryPointPS ?? throw new InvalidOperationException($\"{nameof(InterfaceProcessor)}: At least a pixel or compute shader is expected\");\n            if (entryPointPS == null && entryPointCS == null)\n                throw new InvalidOperationException($\"{nameof(InterfaceProcessor)}: Found both a pixel and a compute shader\");\n\n            var analysisResult = StreamAnalyzer.Analyze(buffer, context);\n            VariableMerger.MergeSameSemanticVariables(table, context, buffer, analysisResult);\n            var streams = analysisResult.Streams;\n\n            var liveAnalysis = new LiveAnalysis();\n            ReadWriteAnalyzer.AnalyzeStreamReadWrites(buffer, context, entryPointPSOrCS.IdRef, analysisResult, liveAnalysis);\n\n            if (entryPointCS != null)\n            {\n                entryPoints.Add(GenerateStreamWrapper(table, buffer, context, ExecutionModel.GLCompute, entryPointCS, analysisResult, liveAnalysis, false));\n            }\n\n            if (entryPointHS != null || entryPointDS != null)\n                context.Add(new OpCapability(Capability.Tessellation));\n            else if (entryPointGS != null)","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/InterfaceProcessor.cs#L132-L168","documentation":"During SPIR-V interface processing for a merged shader, InterfaceProcessor.Process resolves entry points (VS/HS/DS/GS/PS/CS) and requires at least one pixel or compute shader entry point. If neither PSMain nor CSMain exists in the symbol table it throws this InvalidOperationException because the stream/interface analysis pipeline is designed around a fragment- or compute-stage terminal shader.","triggerScenarios":"Calling ShaderMixer.MergeSDSL on a composed ShaderSource whose merged SPIR-V contains only vertex/hull/domain/geometry entry points (no PSMain and no CSMain), so ResolveEntryPoint returns null for both.","commonSituations":"Compiling a shader class chain that ends in a vertex-only or geometry-only stage; forgetting to include the pixel shader mixin in a composition; composing effect shaders where the pixel stage is provided at a later pipeline stage so the merged module legitimately lacks PSMain.","solutions":["Add a pixel or compute shader mixin (one exposing PSMain or CSMain) to the composed ShaderSource before merging","Check the effect composition so the terminal fragment/compute class is actually included in the shader chain","If the stage truly has no PS/CS, bypass InterfaceProcessor.Process or split the merge so stage-only modules are handled separately"],"exampleFix":"// before\nvar mixer = new ShaderMixer();\nmixer.MergeSDSL(new ShaderSource(typeof(MyVertexOnlyShader)), options, log, out var bytecode, out _, out _, out _);\n// after\nvar mixer = new ShaderMixer();\nmixer.MergeSDSL(new ShaderSource(typeof(MyVertexOnlyShader)).Compose(typeof(MyPixelShader)), options, log, out var bytecode, out _, out _, out _);","handlingStrategy":"validation","validationCode":"// before MergeSDSL, ensure a terminal pixel or compute stage exists in the composition\nbool hasPixelOrCompute = composition.Classes.Any(c =>\n    c.GetMethods().Any(m => m.Name is \"PSMain\" or \"CSMain\"));\nif (!hasPixelOrCompute)\n    throw new InvalidOperationException(\"Shader composition must include a pixel (PSMain) or compute (CSMain) stage.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    mixer.MergeSDSL(shaderSource, options, log, out var bytecode, out _, out _, out _);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"At least a pixel or compute shader\"))\n{\n    log.Error(\"Composition lacks a terminal pixel/compute stage: \" + ex.Message);\n}","preventionTips":["Always compose effects around a base class that provides PSMain or CSMain","Write a unit test that compiles each effect composition end-to-end","Review mixin lists when refactoring shader inheritance chains"],"tags":["spirv","shader-compilation","missing-entry-point"],"backgroundTag":"missing-required-argument","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}