CNA-GAP-010: SpriteSortMode::Immediate does not submit each sprite as it is drawn on VULKAN, SDL_GPU, the Direct3D families and WEBGPU, which still batch below the renderer seam
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.
On those renderers a texture rewritten between two Draw calls of an Immediate batch is rasterised with its final contents; CNA documents the VULKAN divergence as deliberate (VULKAN-057), while EasyGL, OPENGL4, FNA3D and SOFTWARE submit per Draw.
- Identifier
CNA-GAP-010- Category
- Functional gap
- Subsystem
- Graphics & renderers
- Status
- Narrowed (partially fixed; describes only what survives)
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::Graphics::SpriteBatch with SpriteSortMode::Immediate
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, OPENGL4 and FNA3D flush the sprite before Draw returns while immediate mode is set (EasyGL and OpenGL 4 end their sprite Draw with if (immediateMode_) FlushBatch(), added by commit 515958a7a on 2026-09-09, SOFTWARE-252, before TARGET), and SOFTWARE rasterises at once. VULKAN (it reads SetImmediateMode only in the compiled-effects build) and SDL_GPU record sprites for replay at present, and the Direct3D families and WEBGPU have no SetImmediateMode override, so they keep accumulating one run per texture until a texture change or End(); texture uploads take effect immediately. docs/xna-4-api-coverage.md records the Vulkan case as “Partial (divergent by decision)” with the measured cost of honouring it, and spritebatch_sort_mode_semantics_test.cpp documents the behaviour without asserting XNA's answer.
Source locations
docs/xna-4-api-coverage.md— SpriteBatch - SpriteSortMode::Immediate sectionmodules/graphics/examples/spritebatch_sort_mode_semantics_test.cpp— legs B and D and their notesmodules/renderers/easygl/src/EasyGLRenderer.cpp— EasyGLSpriteBatchRenderer::Draw ends with 'if (immediateMode_) FlushBatch();' (SOFTWARE-252)modules/renderers/opengl4/src/OpenGL4SpriteBatch.cpp— the same immediate flushmodules/renderers/fna3d/src/Fna3dSpriteBatch.cpp— Queue flushes when immediate_ is set
Evidence
Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry. Behaviour and decision as recorded by CNA; the other renderers' Immediate handling was not re-read.
Narrowed by the independent adversarial audit: the entry originally named EasyGL as a renderer that batches below the seam. Its own sort-mode notes predate commit 515958a7a (2026-09-09, an ancestor of TARGET), which made EasyGL, OpenGL 4 and FNA3D honour immediate submission; the conservation review of the SpriteBatch chapter found the stale statement and the orchestrator re-read the flush code and confirmed the commit is an ancestor of TARGET. Direct3D, WebGPU, Vulkan and SDL_GPU behaviour was read in the same review (no SetImmediateMode override, or the mode read only under compiled effects); nothing was executed.
Independent re-verification: Checked by reading the named sources at 009d40f5, including the git history of each immediate flush; 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, an ancestor of TARGET) made EasyGL honour immediate submission; OPENGL4 gained the same flush in its 2026-09-24 rebuild and FNA3D had it from its introduction. Direct3D, WebGPU, Vulkan and SDL_GPU behaviour was read in the same review (no SetImmediateMode override, or the mode read only under compiled effects). Tests: EasyGL_SpriteBatch_SortModeSemantics and Vulkan_SpriteBatch_SortModeSemantics check order and placement and leg D prices the alternative; none asserts the mid-batch texture-mutation result XNA gives. spritebatch_immediate_submission_contract_test.cpp (SOFTWARE-252) asserts immediate submission across a render-target switch and is registered for EasyGL, OPENGL4 and SOFTWARE only.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
EasyGL_SpriteBatch_SortModeSemantics and Vulkan_SpriteBatch_SortModeSemantics check order and placement, and leg D prices the alternative; none asserts the mid-batch mutation result XNA gives.
Regression test
If the decision is ever reversed, the existing sort-mode fixture's mutation leg should assert the first sprite's old contents.
Blast radius
Games that update a texture (SetData, render-target reuse) between Draw calls of one Immediate batch.
Workaround
End the batch before mutating the texture, as the CNA document recommends.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Known issues
- Functional gap index