CNA-GAP-008: SDL_GPU ShaderEffect draws bind only the first vertex stream, while the limitation report claims all sixteen stream bindings
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit c1c316b9; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
SDL_GPU's custom-effect draw takes its attributes from the first vertex buffer and binds slot 0 only, so other streams (including per-instance streams) are never consumed, and its limitation text does not mention the restriction.
- Identifier
CNA-GAP-008- Category
- Functional gap
- Subsystem
- Graphics & renderers
- Status
- Open
- Verified against
- CNA
c1c316b9(c1c316b9c7a846ce8002809c151fcd1af14942c9) - Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- GraphicsDevice::DrawInstancedPrimitives / DrawIndexedPrimitives with a CNAEXT ShaderEffect applied, on SDL_GPU; GraphicsDevice::GetRendererCapabilityReportEXT()
Expected behaviour
XNA-style hardware instancing binds a per-vertex stream and a per-instance stream (a VertexBufferBinding with an instance frequency) and draws with an effect that reads both. SDL_GPU reports MultiStreamVertexInput true, and its stock routes build one vertex-buffer layout per bound stream. The renderer's English limitation text (GetAdditionalLimitationsTextEXT, part of GetRendererCapabilityReportEXT()) is meant to state what an application cannot branch on.
Actual behaviour at TARGET
With a ShaderEffect applied, DispatchStockDrawEXT and DrawInstancedPrimitivesEx send the draw to QueueCustomEffect3DDrawEXT, which derives every attribute from the first buffer's VertexDeclaration (location = element index, buffer_slot = 0); IssueCustomEffect3DDrawEXT binds exactly one vertex buffer at slot 0. Per-instance streams and additional per-vertex streams in GpuDrawParams::vertexStreams are ignored, and a shader that consumes a location beyond the first declaration is refused with System::NotSupportedException. The limitation text claims all sixteen XNA vertex-stream bindings and says nothing about this ShaderEffect restriction (its sentence "CNAEXT ShaderEffect instancing is not implemented" is accurate: no public API feeds per-instance data to a ShaderEffect on this renderer).
Source locations
modules/renderers/sdl-gpu/src/SdlGpuRenderer.cpp— SdlGpuRenderer::QueueCustomEffect3DDrawEXT (attributes from the first declaration, buffer_slot 0)modules/renderers/sdl-gpu/src/SdlGpuRenderer.cpp— SdlGpuRenderer::IssueCustomEffect3DDrawEXT binds one vertex buffer; DrawInstancedPrimitivesEx SMG-0022 branchmodules/renderers/sdl-gpu/src/SdlGpuRenderer.cpp— SdlGpuRenderer::GetAdditionalLimitationsTextEXTmodules/renderers/webgpu/src/WebGPURenderer.cpp— WebGPURenderer::BuildCustomEffectVertexLayoutsEXT, the WMG-0021 comparison that binds per-instance streams
Evidence
Checked by reading at c1c316b9; not executed. The SMG-0019 to SMG-0022 custom-effect path was added shortly before TARGET. For comparison, WebGPU's BuildCustomEffectVertexLayoutsEXT (WMG-0021) gives a custom-effect draw one layout for the per-vertex stream at slot 0 and one for each per-instance stream. On SDL_GPU the location check prevents silently wrong output when the shader reads a missing location; a stream the shader never reads is simply unused.
Independent re-verification: GraphicsCapability.hpp says a renderer that would silently render from a subset of the bound streams must report MultiStreamVertexInput false. SDL_GPU reports it true for its stock routes, and on the ShaderEffect route the location check refuses a shader that reads a missing location while a stream the shader never reads is unused, so the output is not silently wrong; that is why this entry stays a gap and not a bug.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
No SDL_GPU test draws a ShaderEffect with a per-instance VertexBufferBinding or with two per-vertex streams, and no test reads the instancing sentence of the limitation text (sdlgpu_smoke_test.cpp and sdlgpu_occlusionquery_limitation_test.cpp check other sentences).
Regression test
An SDL_GPU example that draws a ShaderEffect with a per-vertex quad stream plus a per-instance offset stream and reads back two distinct instance positions (it fails today with the location refusal), and an assertion on the limitation text once it states the stream restriction precisely.
Blast radius
SDL_GPU only, and only draws shaded by a CNAEXT ShaderEffect. Stock-effect draws are unaffected; compiled XNA effects use a separate route not assessed here. Code ported from XNA that feeds instance data through a second vertex stream to a custom shader is refused on this renderer.
Workaround
On SDL_GPU, interleave all per-vertex attributes into one stream.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Shader effects: vertex inputs
- Known issues
- Functional gap index