{"record":{"id":"7dd4a65d647fcf18","repo":"stride3d/stride","slug":"opaccesschain-on-patchtype-array-of-streams-can-have-only-1","errorCode":null,"errorMessage":"OpAccessChain on PatchType/array-of-Streams can have only 1 element","messagePattern":"OpAccessChain on PatchType/array-of-Streams can have only 1 element","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Analysis/ReadWriteAnalyzer.cs","lineNumber":192,"sourceCode":"                if (analysisResult.Resources.TryGetValue(structureId, out var resourceInfo3))\n                    resourceInfo3.UsedThisStage = true;\n            }\n            else if (i.Op == Op.OpStreamsSDSL && new OpStreamsSDSL(ref i) is { } streamsInstruction)\n            {\n                streamsInstructionIds.Add(streamsInstruction.ResultId, StreamsKindSDSL.Streams);\n                methodInfo.HasStreamAccess = true;\n            }\n            else if (i.Op == Op.OpAccessChain && new OpAccessChain(ref i) is { } accessChain)\n            {\n                var currentBase = accessChain.BaseId;\n\n                // In case it's a patch or geometry-input-array access, i.e. patch[0] or input[0],\n                // mark the access as being a stream\n                if (patchInstructionIds.TryGetValue(currentBase, out var patchStreamKind))\n                {\n                    var patchVariableId = accessChain.Indexes.Elements.Span[0];\n                    if (accessChain.Indexes.Elements.Length > 1)\n                        throw new InvalidOperationException(\"OpAccessChain on PatchType/array-of-Streams can have only 1 element\");\n                    streamsInstructionIds.Add(accessChain.ResultId, patchStreamKind);\n                }\n                else\n                {\n                    StreamsKindSDSL? currentStreamKind = null;\n                    // In case it's a streams access, mark the stream as being the base\n                    if (streamsInstructionIds.TryGetValue(currentBase, out var streamKind))\n                    {\n                        var streamVariableId = accessChain.Indexes.Elements.Span[0];\n                        var streamInfo = streams[streamVariableId];\n\n                        // Set this base for OpStore/OpLoad stream R/W analysis\n                        currentBase = streamVariableId;\n                        currentStreamKind = streamKind;\n                    }\n\n                    // Any read or write through an access chain will be treated as doing it on the main variable.\n                    // i.e., streams.A.B will share same streamInfo as streams.A","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Analysis/ReadWriteAnalyzer.cs#L174-L210","documentation":"When AnalyzeStreamReadWrites encounters an access chain whose base is a patch variable or a geometry input array, it treats index [0] as the stream selector and only that single index is allowed. An OpAccessChain with more than one index on such a base throws InvalidOperationException.","triggerScenarios":"An OpAccessChain on a patch or array-of-streams base carrying 2+ indexes — e.g. patch[i].member or input[i][j] — inside AnalyzeStreamReadWrites.","commonSituations":"Hull/domain shader code indexing into a patch array and then a member through the same access chain; generated code emitting multi-level access chains on stream arrays instead of decomposing them.","solutions":["Split the access: first index the patch/array element, then access members via separate access chains","Index patches only with a single selector index as the analyzer expects","Check your SDSL source for nested indexing on patch/geometry-input arrays and flatten it","If generated by a transformation, fix the code generator to emit one access chain per level"],"exampleFix":"// before\n// OpAccessChain patch[0].member (2 indexes)\n// after\nvar elem = patch[0];\nvar member = elem.member; // separate access chains","handlingStrategy":"validation","validationCode":"if (accessChain.Indexes.Elements.Length > 1 && patchInstructionIds.ContainsKey(accessChain.Base))\n    throw new InvalidOperationException(\"Patch/array-of-streams access chain must use exactly 1 index\");","typeGuard":null,"tryCatchPattern":"try { analyzer.AnalyzeStreamReadWrites(module); } catch (InvalidOperationException ex) { logger.LogError(ex, \"Bad access chain on patch/stream array\"); throw; }","preventionTips":["Index patches with a single selector index in SDSL","Decompose multi-level accesses into separate access chains in code generators","Add IR validation between transformations that create access chains"],"tags":["shader","spirv","streams-analysis"],"backgroundTag":"invalid-argument-value","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"}