{"record":{"id":"b30973ae1a48cd8e","repo":"stride3d/stride","slug":"cbuffer-name-have-a-mix-of-stage-and-non-stage-members","errorCode":null,"errorMessage":"cbuffer {Name} have a mix of stage and non-stage members","messagePattern":"cbuffer (.+?) have a mix of stage and non-stage members","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/ShaderElements.cs","lineNumber":344,"sourceCode":"        foreach (var cbMember in Members)\n        {\n            cbMember.TypeName.ProcessSymbol(table);\n            cbMember.Type = cbMember.TypeName.Type;\n        }\n\n        var pointerType = new PointerType(Type!, Specification.StorageClass.Uniform);\n\n        isStaged = null;\n        for (var index = 0; index < Members.Count; index++)\n        {\n            var member = Members[index];\n\n            // Use first member as reference\n            if (isStaged == null)\n                isStaged = member.IsStaged;\n            // Make sure IsStaged for all members match the first member (they're all the same)\n            if (isStaged != member.IsStaged)\n                throw new InvalidOperationException($\"cbuffer {Name} have a mix of stage and non-stage members\");\n        }\n\n        var constantBufferType = (ConstantBufferSymbol)Type!;\n\n        // We try to avoid clash in case multiple cbuffer TYPE with same name\n        // The variable itself is handled by adding a .0 .1 etc. in Shader.RenameCBufferVariables()\n        int tryCount = 0;\n        var typeName = constantBufferType.Name;\n        while (!table.DeclaredTypes.TryAdd(constantBufferType.ToId(), Type!))\n        {\n            typeName = $\"{typeName}_{++tryCount}\";\n            Type = constantBufferType = constantBufferType with { Name = typeName };\n        }\n\n        context.DeclareCBuffer(constantBufferType, context.Bound++);\n\n        var sid = new SymbolID(Name, SymbolKind.CBuffer, Storage.Uniform);\n        Symbol = new Symbol(sid, pointerType, context.Bound++, OwnerType: table.CurrentShader);","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/ShaderElements.cs#L326-L362","documentation":"CBuffer.ProcessSymbol walks the cbuffer's members and records whether the first member 'IsStaged'; every subsequent member must match. If some members are stage-bound (declared inside a stage block) and others are not, the cbuffer layout is ambiguous for SPIR-V emission, so an InvalidOperationException is thrown naming the cbuffer.","triggerScenarios":"Declaring a cbuffer where some members come from stage-specific declarations and others from regular declarations, e.g. mixing members declared under different stages or merging cbuffers via shader composition/inheritance so staged and non-staged members land in the same cbuffer.","commonSituations":"Shader composition where a base class declares a cbuffer and a mixin/stage adds members to it; large codebases where a cbuffer name is reused across stage-scoped and global scopes.","solutions":["Split the cbuffer into two cbuffers: one with only staged members and one with only non-staged members.","Ensure members added via composition/inheritance go into a differently named cbuffer than the base's stage-bound cbuffer.","Make all members of the offending cbuffer consistently staged (or consistently non-staged) in the shader source."],"exampleFix":"// before\ncbuffer Mixed { float a; stage float b; };\n// after\ncbuffer NonStaged { float a; };\nstage cbuffer Staged { float b; };","handlingStrategy":"validation","validationCode":"var staged = members.Select(m => m.IsStaged).Distinct().ToList();\nif (staged.Count > 1) throw new InvalidOperationException($\"cbuffer {name} mixes staged and non-staged members\");","typeGuard":null,"tryCatchPattern":"try { cbuffer.ProcessSymbol(table, context); }\ncatch (InvalidOperationException ex) when (ex.Message.StartsWith(\"cbuffer\")) { log(ex.Message); splitCBuffer(); }","preventionTips":["Keep each cbuffer uniformly staged or non-staged.","Use distinct cbuffer names per stage scope and per mixin.","After composition/inheritance, audit merged cbuffers for member provenance."],"tags":["shader-parsing","sdsl","cbuffer","symbol-processing"],"backgroundTag":"invalid-state-transition","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"}