Skip to content

Mesh runs while occlusion query counting is on: in Donkey Kong Bananza no draw had joined a run - #1099

Closed
AdyaGMD wants to merge 1 commit into
seedds:mainfrom
AdyaGMD:geo-mesh-runs-queries
Closed

AdyaGMD wants to merge 1 commit into
seedds:mainfrom
AdyaGMD:geo-mesh-runs-queries

Conversation

@AdyaGMD

@AdyaGMD AdyaGMD commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

What and why

Follow-up to #1087 (landed as 84ec307): in Donkey Kong Bananza its mesh draw runs never form. The log reads 0 of them in 0 runs in every heavy frame.

Cause: geo_mesh_run_fits refused runs while samples-passed counting was on (g_vis_counting), and counting stays on for the rest of the game after the first samples-passed reset: mtl_exec sets the flag and nothing clears it (only a deferred-pass replay does, for its own replay). Bananza resets that counter once, so every geometry draw of its depth prepass was dispatched alone, although the heavy frames have no samples-passed report at all.

Fix: runs are allowed while counting is on. A report ends a run before it executes (it is not in geo_mesh_keeps), and a pass keeps one query slot (g_vis_cur), so each query's interval holds whole runs, never part of one. Whether any of its samples passed is then the same in any order of the run's draws (the nearest fragment of each pixel passes whatever the order), which is what occlusion tests and conditional rendering ask; only the exact count can differ by fragments another order rejects. The other conditions stay (no colour or stencil writes, an order-free depth test, a fragment shader with no resources). A draw that cannot join a run now logs why, once per reason (geometry mesh run: a draw of vertex … draws alone: …), so a refusal cannot hide again.

Reference: Mesh geometry draws gathered into one dispatch, updated.

Checked

Docs

  • The finding is in its docs/reference/shaders.md entry; python3 dm/tools/docs_symptoms.py was run
  • A chain tool changed (no)
  • No game files, keys or data extracted from a game in the change or the attachments

🤖 Generated with Claude Code

…a no draw had joined a run

The first version refused runs while samples-passed counting was on, and counting stays on for the rest of the game after the first reset (mtl_exec sets g_vis_counting and
nothing clears it). Bananza resets that counter once, so its log read "0 of them in 0 runs" in every heavy frame. A report ends a run (geo_mesh_keeps) and a pass keeps one query
slot, so a query's interval holds whole runs, and whether any of its samples passed is the same in any order (the nearest fragment of each pixel passes whatever the order); only
the exact count can differ. A draw that cannot join a run now logs why, once per reason.

In the heavy area (handheld, MetalFX upscaler): 142,000-155,000 mesh draws in 300 runs per 300 frames, one run of about 475 draws a frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation labels Nov 3, 2026
@seedds

seedds commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Thanks. The diagnosis is right: g_vis_counting never clears after the first samples-passed reset, so the old rule meant no runs in any game that had used an occlusion query. The any-passed argument holds too. I'm not landing it tonight only because nothing here can exercise it: no local game draws mesh runs with counting on (NieR's route needs its opening-flight save, which isn't on this machine), and the run changes exact counts when depth writes are on. Two things would let it land: a t_geometry_mesh case with a samples-passed reset before the draws (one run with depth writes, a report after it, the count nonzero and the picture the same as single draws), and a before/after screenshot of the Bananza scene you measured.

seedds added a commit that referenced this pull request Nov 3, 2026
…ays on for the rest of a game after its first reset, so Donkey Kong Bananza's depth prepass drew every geometry draw alone (#1099)

A report ends a run before it executes and a pass keeps one query slot, so a
query's interval holds whole runs; whether any of its samples passed is the
same in any order. A refused draw's reason is logged once. t_geometry_mesh
draws its runs again with a counter reset before them and a report after: the
draws join runs and the report holds every sample they passed.

Co-authored-by: AdyaGMD <114347520+AdyaGMD@users.noreply.github.com>
@seedds

seedds commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Landed on main as 53ab7a9, with you credited as co-author, plus the test I asked for, which I wrote myself: t_geometry_mesh now draws its runs again with a samples-passed reset before them and a report after. The draws must join runs, and the report must equal the pixels they wrote. It fails on main's nvn_geometry.inc and passes with your change (runs, single draws, encode thread). regress.py check on BotW, Wonder and TotK showed no new red lines and unchanged fps. Thanks!

@seedds seedds closed this Nov 3, 2026
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…ays on for the rest of a game after its first reset, so Donkey Kong Bananza's depth prepass drew every geometry draw alone (seedds#1099)

A report ends a run before it executes and a pass keeps one query slot, so a
query's interval holds whole runs; whether any of its samples passed is the
same in any order. A refused draw's reason is logged once. t_geometry_mesh
draws its runs again with a counter reset before them and a report after: the
draws join runs and the report holds every sample they passed.

Co-authored-by: AdyaGMD <114347520+AdyaGMD@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants