CNA-BUG-112: On VULKAN SpriteBatch draws are never tagged with the active OcclusionQuery: a sprite-only Begin/End span reports PixelCount 0, and sprites in a mixed span are counted only by record order
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.
Only 3D draws are tagged with the active query (PushPending3DDraw is the only tagging site), so a query whose span holds only SpriteBatch draws reports 0: taggedDraws_ stays 0 and IsComplete answers complete with zero pixels without asking the GPU. Sprite batches never open or close a query bracket, so whether one is counted depends on where it lands among the tagged 3D draws.
- Identifier
CNA-BUG-112- Category
- Bug
- Subsystem
- Graphics & renderers
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Medium (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::Graphics::OcclusionQuery with SpriteBatch draws on VULKAN
Expected behaviour
XNA's occlusion query counts every sample written between Begin and End, whatever issued the draw; SpriteBatch-drawn visibility probes (for example a flare occlusion test) rely on it. Vulkan's own documentation in CNA lists occlusion queries as supported without this restriction.
Actual behaviour at TARGET
In VulkanRenderer.cpp VulkanOcclusionQueryRenderer::Begin sets activeOcclusionQuery_, and PushPending3DDraw copies it into each queued 3D draw and counts it in taggedDraws_. VulkanSpriteBatchRenderer::End pushes an untagged PendingBatch snapshot (the exception is a compiled-effect sprite run, only in a CNA_VULKAN_COMPILED_EFFECTS build, which is queued as a Pending3DDraw and so is tagged). RecordCommandBuffer opens vkCmdBeginQuery only at a tagged 3D draw and closes it only at an untagged or differently tagged 3D draw or at the end of the pass segment (closeOpenQuery3D). Consequences: a sprite-only span reports 0; a sprite recorded before the span's first tagged 3D draw is not counted; a sprite recorded after a tagged 3D draw in the same pass falls inside the still-open bracket and is counted, even if it was issued after End().
Source locations
modules/renderers/vulkan/src/VulkanRenderer.cpp— VulkanRenderer::PushPending3DDraw (the only tagging site) and VulkanOcclusionQueryRenderer::Begin
Evidence
Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry.
Independent re-verification: Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry. The bracket-stays-open consequence for mixed spans was derived from the record loop (drawFamiliesInOrder replays sprite runs between 3D runs, and only draw3DFor and closeOpenQuery3D issue vkCmdEndQuery). CNA's own plan_vulkan_parity.md VKPAR-0026 states that Vulkan records a query only around the 3D draws tagged with it, and makes a query with no tagged draw complete with 0 pixels 'as XNA reports for a query that drew nothing', which is not true of a span that drew sprites. The Vulkan occlusion fixtures (Vulkan_OcclusionQuery_PixelCount, Vulkan_OcclusionQuery_Precision) draw 3D geometry only.
Focused reproduction
// Illustrative; not compiled or run for this entry.
query.Begin();
batch.Begin();
batch.Draw(probeTexture, Rectangle(100, 100, 8, 8), Color::White);
batch.End();
query.End();
// later: query.getPixelCountProperty() -> XNA and EasyGL: 64, VULKAN: 0
Current tests
Vulkan occlusion fixtures draw 3D geometry only.
Regression test
A renderer-neutral fixture that counts an 8x8 SpriteBatch quad inside a query, registered for VULKAN and EasyGL.
Blast radius
Games that measure sprite visibility with occlusion queries on VULKAN.
Workaround
Draw the probe as 3D geometry (a textured quad through BasicEffect).
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Vulkan internals: draw
- Deep dives
- Vulkan deferred state: occlusion queries
- Known issues
- Bug index