stride3d/stride · error · InvalidOperationException
InterfaceProcessor: Found both a pixel and a compute shader
Error message
InterfaceProcessor: Found both a pixel and a compute shader
What it means
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.
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
Example fix
// before var shader = new ShaderSource(typeof(MyMaterial)).Compose(typeof(MyComputeKernel)); // has PSMain + CSMain // after var shader = new ShaderSource(typeof(MyMaterial)); // graphics pass only; compile the kernel separately
Defensive patterns
Strategy: validation
Validate before calling
var hasPixel = composition.Classes.Any(c => c.GetMethods().Any(m => m.Name == "PSMain"));
var hasCompute = composition.Classes.Any(c => c.GetMethods().Any(m => m.Name == "CSMain"));
if (hasPixel && hasCompute)
throw new InvalidOperationException("Cannot mix pixel and compute stages in one merged shader; split into separate passes."); Try / catch
try
{
mixer.MergeSDSL(shaderSource, options, log, out var bytecode, out _, out _, out _);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Found both a pixel and a compute shader"))
{
log.Error("Mixed graphics+compute composition: " + ex.Message);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Can't cast from to (larger matrix)
- Can't cast from to
- Can't cast from to
- Can't figure array output size for tessellation shader
- Can't OpLoad with cbuffer
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/71b646cc2ba62c75.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/InterfaceProcessor.cs:152
}
public Result Process(SymbolTable table, SpirvBuffer buffer, SpirvContext context)
{
// OpEntryPoint emission is deferred to allow fixups (e.g. adding dummy DS inputs for HS-internal outputs)
var entryPoints = new List<EntryPointInfo>();
compositionFunctions = CollectCompositionFunctions(buffer);
var entryPointVS = ResolveEntryPoint(table, "VSMain");
var entryPointHS = ResolveEntryPoint(table, "HSMain");
var entryPointDS = ResolveEntryPoint(table, "DSMain");
var entryPointGS = ResolveEntryPoint(table, "GSMain");
var entryPointPS = ResolveEntryPoint(table, "PSMain");
var entryPointCS = ResolveEntryPoint(table, "CSMain");
var entryPointPSOrCS = entryPointCS ?? entryPointPS ?? throw new InvalidOperationException($"{nameof(InterfaceProcessor)}: At least a pixel or compute shader is expected");
if (entryPointPS == null && entryPointCS == null)
throw new InvalidOperationException($"{nameof(InterfaceProcessor)}: Found both a pixel and a compute shader");
var analysisResult = StreamAnalyzer.Analyze(buffer, context);
VariableMerger.MergeSameSemanticVariables(table, context, buffer, analysisResult);
var streams = analysisResult.Streams;
var liveAnalysis = new LiveAnalysis();
ReadWriteAnalyzer.AnalyzeStreamReadWrites(buffer, context, entryPointPSOrCS.IdRef, analysisResult, liveAnalysis);
if (entryPointCS != null)
{
entryPoints.Add(GenerateStreamWrapper(table, buffer, context, ExecutionModel.GLCompute, entryPointCS, analysisResult, liveAnalysis, false));
}
if (entryPointHS != null || entryPointDS != null)
context.Add(new OpCapability(Capability.Tessellation));
else if (entryPointGS != null)
context.Add(new OpCapability(Capability.Geometry));
View on GitHub (pinned to 96fad776d2)