{"record":{"id":"71b646cc2ba62c75","repo":"stride3d/stride","slug":"interfaceprocessor-found-both-a-pixel-and-a-compute-shader","errorCode":null,"errorMessage":"InterfaceProcessor: Found both a pixel and a compute shader","messagePattern":"InterfaceProcessor: Found both a pixel and a compute shader","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/InterfaceProcessor.cs","lineNumber":152,"sourceCode":"        }\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)\n                context.Add(new OpCapability(Capability.Geometry));\n","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/InterfaceProcessor.cs#L134-L170","documentation":"InterfaceProcessor.Process throws this when it cannot find both a pixel and a compute shader entry point, despite the message text. It is a sanity guard placed after the ??-chain: if both entryPointPS and entryPointCS were non-null the earlier ??-fallback already picked one, so this check flags the contradictory configuration where a single merged module is being treated as both a graphics and a compute shader.","triggerScenarios":"Merging SDSL shader sources whose SPIR-V contains both PSMain and CSMain entry points (entryPointPS != null && entryPointCS != null) — the guard fires because a merged module must be either a graphics pipeline effect or a compute effect, not both.","commonSituations":"A composed shader accidentally mixes in both a pixel-stage class and a compute-stage class; shared mixin libraries that declare stage entry points being composed with the wrong base class; copy-pasting a compute kernel into a material shader.","solutions":["Remove the compute shader mixin from the composition if this is a graphics effect (or remove the pixel shader if it is a compute effect)","Split the shader into two separate ShaderSource compositions — one for the graphics pass, one for the compute pass","Rename one of the conflicting entry points so ResolveEntryPoint only finds the intended stage"],"exampleFix":"// before\nvar shader = new ShaderSource(typeof(MyMaterial)).Compose(typeof(MyComputeKernel)); // has PSMain + CSMain\n// after\nvar shader = new ShaderSource(typeof(MyMaterial)); // graphics pass only; compile the kernel separately","handlingStrategy":"validation","validationCode":"var hasPixel  = composition.Classes.Any(c => c.GetMethods().Any(m => m.Name == \"PSMain\"));\nvar hasCompute = composition.Classes.Any(c => c.GetMethods().Any(m => m.Name == \"CSMain\"));\nif (hasPixel && hasCompute)\n    throw new InvalidOperationException(\"Cannot mix pixel and compute stages in one merged shader; split into separate passes.\");","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(\"Found both a pixel and a compute shader\"))\n{\n    log.Error(\"Mixed graphics+compute composition: \" + ex.Message);\n}","preventionTips":["Keep compute kernels in their own effect/pass, never composed with material shaders","Avoid mixins that unconditionally declare stage entry points","Audit compositions after adding shared utility shader classes"],"tags":["spirv","shader-compilation","conflicting-stages"],"backgroundTag":"conflicting-config-options","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"}