CNA-GAP-008: SDL_GPU ShaderEffect draws bind only the first vertex stream, and the limitation report misstates ShaderEffect instancing

CNA snapshot 009d40f5  ·  Known Issues › Functional gaps  ·  source links pinned to 009d40f5

✓

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 009d40f5; 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 are never consumed, while its limitation text calls ShaderEffect instancing unimplemented although instance-indexed draws work.

Identifier
CNA-GAP-008
Category
Functional gap
Subsystem
Graphics & renderers
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
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. Instanced ShaderEffect draws do work when the shader reads per-instance data from a storage buffer by gl_InstanceIndex (SMG-0022, the engine layer's particle route). The limitation text nevertheless says "CNAEXT ShaderEffect instancing is not implemented", which is wrong for that route and silent about the stream restriction.

Source locations

Evidence

Checked by reading at 009d40f5; 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

The storage-buffer instancing route is exercised through the particle system in CnaGraphicsExtTests (CNA records SDL_GPU at 931/0/31 of 962, SMG-0042). 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 and storage-buffer instancing 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, give a ShaderEffect its per-instance data through a storage buffer indexed by gl_InstanceIndex, or interleave all per-vertex attributes into one stream.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.