Skip to content

mdi_wait patches the raw pool table words before its mid-submission commit - #2330

Open
dean0396 wants to merge 1 commit into
seedds:mainfrom
dean0396:pr-mdiwait
Open

dean0396 wants to merge 1 commit into
seedds:mainfrom
dean0396:pr-mdiwait

Conversation

@dean0396

Copy link
Copy Markdown
Contributor

What and why

mdi_wait (dm/gfx/nvn/nvn_mtl.m:13371) ends the render pass and commits the main command buffer in the middle of a submission, then waits for the GPU so the replay can read on the CPU what the GPU wrote: an indirect-count multi-draw's count, a geometry draw's indirect record, an indirect dispatch's grid when its threadgroup memory spills (tg_spill), or memory for DM_NVN_UBP_CHECK. The other two commits of g_cb, mtl_sub_commit (2390) and sub_cut (15004), call gmem_commit_patch() before they end the command buffer. mdi_wait doesn't (13374 goes straight from up_flush() to res_commit()).

So the window that 339d9f2 (PR #1427) closed for whole and segment commits is still open at this one:

  • An argument block encoded before the wait keeps the raw pool table it was encoded with. A pool made between that encode and the wait's commit isn't in the table the GPU searches, and a raw read of it gets 0.
  • The block's word stays in the noted list, so the next commit (a segment or the end of the submission) rewrites it after the GPU has already run the block. It also moves the block's write log (NvGmlLog) to the newer table, so gml_read decodes bits the shader set against the old table against the new one.

The fix is the same call in the same place as sub_cut: gmem_commit_patch() after up_flush() and before res_commit() / endCommandBuffer. It clears the noted list, so the next commit only patches blocks encoded after the wait. In the host that's the one call and its comment, plus the comment above NvGmlLog, which now lists the count wait's commit too.

I found this by reading the code, not in a game. Ocarina of Time, the game I play, never reaches mdi_wait (no "indirect-count multi-draw" line in its logs), so this is for games that do. Going by docs/reference/rendering.md, those are geometry draws that can't be masked on the GPU (Donkey Kong Bananza as a level opens) and draws that need a CPU count (quads, fans, 8-bit indices).

Overlaps:

Checked

  • Tests, on upstream/main 8fb5ffe:
    • t_gmem_words with the new case (a word that holds the table from before a pool was made, then mdi_wait(), must point at a table that holds that pool, and no word may be left noted after it): with only the test change, 18 checks, 2 failed (the two new ones). With the fix, 18 checks, 0 failed, 5 runs in a row.
    • make -C dm/gfx/tests GAME=0400486024DEC000 run: 135 of 136 tests pass. Among them are t_large_mdi, t_geometry and t_tg_spill, which also go through mdi_wait. They don't use the raw pool table, so they only show that nothing else broke. The other one, t_feedback, fails its check "with nothing running: a commit" now and then, on an unmodified export of main too (3 of 6 runs on main, 4 of 6 with this change). It doesn't reach mdi_wait.
    • python3 -m unittest dm/tools/tests/test_docs.py: 6 tests, OK. I ran python3 dm/tools/docs_symptoms.py, and the symptom index doesn't change (only the entry's Fix and Check lines did).
    • The Mac host builds: make -C dm/host GAME=0400486024DEC000 dmhost.
  • Games run: none on main. Ocarina of Time, the only game built here, doesn't reach mdi_wait. The same change has been in my own fork's builds since November 8. That's where I play Ocarina, so it never runs there either.
  • Devices and OS: M3 Max, macOS 26.7.2.
  • regress.py: not run. Only Ocarina of Time is built here, and it doesn't reach this path, so a report from it couldn't show anything. The change only does something when a submission reaches mdi_wait with words noted since the last commit.
  • Also not run: lint_c.py (no clang-tidy on this machine), iOS.

Docs

  • The finding is in its game note or its docs/reference/<subsystem>.md entry, and python3 dm/tools/docs_symptoms.py was run (the symptom index): the Fix and Check lines of "Raw pool table words that predate the pools" in docs/reference/shaders.md now name the count wait's commit
  • A chain tool changed: dmsh_chain.py --header was run and dm/gfx/nvn/dmsh_chain.h is part of the change (no chain tool changed)
  • No game files, keys or data extracted from a game in the change or the attachments

…ommit, as sub_cut and mtl_sub_commit do: blocks encoded before an indirect-count multi-draw's count wait no longer reach the GPU with a pool table that lacks pools made since they were encoded (the window 339d9f2, PR seedds#1427, closed for whole and segment commits), nor have their words rewritten after the GPU ran them. mdi_wait ends the render pass and commits the main command buffer in the middle of a submission (endCommandBuffer, rec_commit) so it can wait for the GPU and read on the CPU what the GPU wrote: a multi-draw's count, a geometry draw's indirect record, an indirect dispatch's grid (tg_spill), or memory for DM_NVN_UBP_CHECK. It did not call gmem_commit_patch(), so the table words of that command buffer's argument blocks (gmem_slot_note) stayed as they were encoded: a pool made between their encode and this commit was missing from the table the GPU searched there, and a raw read of it got 0. The words also stayed in the noted list, so the next commit (a segment or the end of the submission) rewrote them after the GPU had already run those blocks, and moved their write logs (NvGmlLog) to a table their bits were not taken against. gmem_commit_patch() now runs after up_flush() and before res_commit() / endCommandBuffer, where sub_cut has it. It clears the noted list, so the next commit patches only the blocks encoded after the wait. t_gmem_words gets the case: a word that holds the table from before a pool was made, then mdi_wait(), points at a table that holds that pool, and no word is left noted after it. The reference entry (Raw pool table words that predate the pools) and the comment above NvGmlLog name the count wait's commit. Found by reading the code, not seen in a game. Checked: t_gmem_words 18 checks, 2 failed (the two new ones) with only the new case and 18, 0 failed with the nvn_mtl.m change (5 runs); make -C dm/gfx/tests GAME=0400486024DEC000 run, 135 of 136 tests pass (among them t_large_mdi, t_geometry and t_tg_spill, which also reach mdi_wait), and the other, t_feedback's "with nothing running: a commit", which does not reach mdi_wait, fails now and then on unmodified main too (3 of 6 runs on main, 4 of 6 here); test_docs; the Mac host builds (make -C dm/host GAME=0400486024DEC000 dmhost). Not run: a game that reaches mdi_wait (Ocarina of Time logs no indirect-count multi-draw wait), regress.py, lint_c.py (no clang-tidy here), iOS
@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation labels Nov 11, 2026
Aznracer pushed a commit to Aznracer/Mighty-Green-Kokiri-Kid that referenced this pull request Nov 11, 2026
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.

1 participant