CNA-BUG-280: SpriteSortMode::Immediate is batched on SDL_GPU and WEBGPU: a texture changed between two Draw calls of one Immediate batch shows its new contents in both sprites

CNA snapshot c6dec91f  ·  Known Issues › Current bugs  ·  source links pinned to c6dec91f

✓

Evidence basis: source-verified at the pinned commit; recorded by CNA's own run (not repeated here); tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit c6dec91f; 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 and WEBGPU accept Immediate but defer its stock sprites to a later flush, so a texture rewritten between two Draw calls is rasterised with its final contents; EasyGL, SOFTWARE, FNA3D and, since a fix awaiting Windows validation, DIRECTX9 submit per Draw.

Identifier
CNA-BUG-280
Category
Bug
Subsystem
Graphics & renderers
Status
Narrowed (partially fixed; describes only what survives)
Verified against
CNA c6dec91f (c6dec91fbf3ef5440f60e5329e4a4758b2d80c22)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Recorded by CNA: CNA's own recorded run, not repeated here
Tests touching this area
Yes: see Current tests
Affected contract
Microsoft::Xna::Framework::Graphics::SpriteBatch with SpriteSortMode::Immediate on SDL_GPU and WEBGPU

Expected behaviour

XNA defines Immediate as drawing each sprite at its own draw call rather than at End(), so a texture mutated between two Draw calls leaves the first sprite showing the old contents.

Actual behaviour at TARGET

CNA passes the mode to the renderer, and what the renderer does is per family. EasyGL and FNA3D flush the sprite before Draw returns while immediate mode is set (EasyGL ends its sprite Draw with if (immediateMode_) FlushBatch(), commit 515958a7a, SOFTWARE-252), SOFTWARE rasterises at once, and DIRECTX9's sprite renderer flushes per Draw since 3b6a2eaf3 (CNA-DEMOS-ISSUE-003, whose Windows runtime validation CNA still lists as pending). SDL_GPU stores the mode and reads it only on its compiled-effect sprite route; stock sprites are recorded for replay at the next flush. WEBGPU's per-Draw FlushPendingSpritesEXT acts only in a CNA_WEBGPU_COMPILED_EFFECTS build and only on compiled-effect sprites; stock sprites stay queued. On both, texture uploads take effect immediately, so the earlier sprite of the batch samples the later contents.

CNA records this as its one current defect (handoff.md section 5.1, low priority, reproduced 2026-10-17): with EasyGL_SpriteBatch_SortModeSemantics, run on SDL_GPU as SdlGpu_CrossFamily_SpriteBatch_SortModeSemantics and elsewhere with CNA_GRAPHICS_RENDERER, leg B prints blue/blue where XNA gives red/blue. The test passes, because spritebatch_sort_mode_semantics_test.cpp reports the leg without asserting XNA's answer.

Source locations

Evidence

Checked by reading the named sources at c6dec91f; nothing was built or executed for this entry.

Narrowed by the independent adversarial audit: the entry originally named EasyGL as a renderer that batches below the seam. Commit 515958a7a (2026-09-09) made EasyGL honour immediate submission (FNA3D already did); nothing was executed.

Re-verified at c6dec91f: SetImmediateMode is overridden by every sprite renderer; SdlGpuRenderer.cpp reads immediateMode_ only on the compiled-effect sprite route, WebGPURenderer.cpp's per-Draw flush is compiled only with CNA_WEBGPU_COMPILED_EFFECTS, and D3D9SpriteBatch.cpp calls FlushBatch() per Draw in immediate mode (3b6a2eaf3). The behaviour on SDL_GPU and WEBGPU is CNA's own record in handoff.md section 5.1 (2026-10-17), not re-run here. CNA classes it as a defect; the deliberate-divergence record this entry used to cite is no longer in the tree.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

EasyGL_SpriteBatch_SortModeSemantics and SdlGpu_CrossFamily_SpriteBatch_SortModeSemantics (spritebatch_sort_mode_semantics_test.cpp) check order and placement and print the mid-batch mutation result (leg B) without asserting it. spritebatch_immediate_submission_contract_test.cpp (SOFTWARE-252) asserts immediate submission across a render-target switch and is registered for EasyGL and SOFTWARE only.

Regression test

Make leg B of the sort-mode fixture assert the first sprite's old contents (red/blue) on every family, and register the immediate-submission contract for SDL_GPU and WEBGPU.

Blast radius

Games that update a texture (SetData, render-target reuse) between Draw calls of one Immediate batch on SDL_GPU (the default renderer on Windows, macOS and iOS) or WEBGPU. Deferred and sorted modes, and the other renderers, are unaffected.

Workaround

End the batch before mutating the texture.

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

Known issues
Bug index