Skip to content

Raw pool table words follow the pools at commit: a pool made after a raw shader was encoded no longer reads 0 - #1427

Closed
lunks wants to merge 3 commits into
seedds:mainfrom
lunks:pr/gmem-words-at-commit
Closed

lunks wants to merge 3 commits into
seedds:mainfrom
lunks:pr/gmem-words-at-commit

Conversation

@lunks

@lunks lunks commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

What and why

An argument block holds the address of the raw pool table that was current when the block was encoded (gmem_map_gpu). A memory pool made after that, later in the same submission or on another thread while it is encoded, is not in that table, and the GPU reads the block after the commit. A raw read of that pool then gets 0, the failure that hung agl's task shaders in Ocarina of Time (loop (0 - 1) >> 5 times; the table in #1360 fixes the pools that did not fit it, this is about the ones that were made too late for the block's table).

How often it happens (one boot, not a benchmark): on the author's stack, loading Ocarina of Time's outdoor save (profile in #1299), 5,729 of 154,107 table words in argument buffers held an older table than the current one by frame 5,100, and 2,582 held the current one while it lagged the pools. A save load makes more than 4,000 pools in a burst.

Change: the idea is @uncraftablexd's in #1407 (rewrite the words at commit). The words are noted as they are encoded (gmem_slot_note) and, at each commit of the command buffer, whole or segment (mtl_sub_commit, sub_cut), pointed at a table of the pools as they are then (gmem_commit_patch), before the GPU can read them. A new table is built when the pools changed; it is not refilled in place, as earlier segments may be reading the old one, and the replaced one goes through the deferred release as before. The write log of these blocks lists pages, not table entries, so it needs nothing here. The memory report prints the words, those that held an older table than the current one, and those that held the current one behind the pools (raw pool table words at commit).

Whether this is what is left of the hang in #1326 (@dacONJA's, minutes after a clean table growth, 4,098 ranges, no overflow line) is not known: nothing here reproduced it. The counts say the window exists in Ocarina of Time; they do not say a hang came through it.

Related: #1360 (the table's size; merges with this one except for one line of dm/gfx/tests/Makefile, per git merge-tree), #1407 (the same rewrite in its stack, with an in-place refill of a table first bound in the same submission, which this does not do).

Reference entry: Raw pool table words that predate the pools. The symptom index is regenerated.

Checked

  • Tests (Apple M5 Pro, macOS 27, niced; make -C dm/gfx/tests GAME=010015100B514000 ...): check_t_gmem_words (new, 13 checks, 0 failed): an unchanged table leaves the words alone; a pool made after two words were noted is in the table they are pointed at and a new table was built; an older table and one that lags the pools are rewritten and counted; a commit with no words changes nothing. With the rewrite removed, 2 checks fail. check_t_pool_chunks 75 checks, 0 failed. test_docs.py, test_gfx_tests_makefile.py and test_env_switches.py pass; docs_symptoms.py --check current; lint_c.py tidy on nvn_mtl.m and t_gmem_words.m: 0 file/check pairs above the baseline.
  • Games run: one boot of Ocarina of Time (v131072, M5 Pro) on the author's stack, which carries this change in another form (it also moves the write log of a table that grows, which Raw global memory's pool table grows with the pools: pools past 4,095 were left out and hung the GPU in Ocarina of Time #1360's table needs and main's page log does not): no GPU errors. It was not steady outdoors, and there is no run of this branch itself: it needs [WIP] The Legend of Zelda: Ocarina of Time (Switch 2, 0400486024DEC000, v0, SDK 22.2.8) runs on the Mac through the title movie into Kokiri Forest #1299's profile.
  • Not run: the rest of make -C dm/gfx/tests run, the host tests, regress.py, any game on main. The change only rewrites words in argument blocks that hold a table address, and only when it differs from the current one, so a game with raw global memory shaders is the only one that can see a difference.
  • Devices and OS: Apple M5 Pro, macOS 27.

Docs

  • The finding is in docs/reference/shaders.md and python3 dm/tools/docs_symptoms.py was run
  • No chain tool changed
  • No game files, keys or data extracted from a game in the change

…raw shader was encoded no longer reads 0

An argument block holds the pool table that was current when it was encoded. A pool made after that, later in the submission or on another thread while it is encoded, is not in it, and a raw read of that pool gets 0, the failure that hung agl's task shaders. On the author's stack, loading Ocarina of Time's outdoor save, 5,729 of 154,107 table words in argument buffers held an older table than the current one by frame 5,100, and 2,582 held the current one behind the pools.

As seedds#1407 does in its stack, the words are noted as they are encoded (gmem_slot_note) and rewritten at each commit, whole or segment (gmem_commit_patch), before the GPU can read them. A new table is built when the pools changed, not refilled in place, as earlier segments may be reading the old one; the replaced one goes through the deferred release as before. The write log of these blocks lists pages, not table entries, so it needs nothing here. The memory report prints the words, those that held an older table and those that held the current one behind the pools.

Whether this is what is left of the hang in seedds#1326, minutes after a clean table growth, is open: nothing here reproduced it.

t_gmem_words, 13 checks: an unchanged table leaves the words alone; a pool made after two words were noted is in the table they are pointed at, and a new table was built; an older table and one that lags the pools are rewritten and counted; a commit with no words changes nothing. With the rewrite removed, 2 checks fail. t_pool_chunks 75 checks pass; test_docs, test_gfx_tests_makefile, test_env_switches, docs_symptoms.py --check; lint_c.py tidy on nvn_mtl.m and t_gmem_words.m: 0 file/check pairs above the baseline.
table_has walked DMSH_GMEM_MAP_ENTRIES entries. A table that grows with its pools (seedds#1360) has more, and a pool past entry 4,095 would not have been found. It walks the buffer's length now, which is the same 4,096 entries on main.

Pointed out by deanpektas27 on seedds#1360.

Checked: check_t_gmem_words, 13 checks, 0 failed.
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
…rina of Time, with the reasons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
…ollows its table at the commit

Main's b1d7c70 made the raw write log a bitmap of the table's entries and read it against the table captured when the log was taken (gml_take). The commit-time repointing of a block's table word (gmem_commit_patch) then made the shader write bits of the newer table while gml_read decoded them against the older one. NvGmlLog keeps a log with its table, the slot note carries the log, the commit patch updates it, and encode_args takes the table for both slots once (raw_map), as a pool coming in between the two slots would otherwise give them different tables.

t_gmem_words follows: the slot signature, its pools are addressed (main lists only those), and a new check that the log's table moves with the word.

Checked: check_t_gmem_words (16 checks, 0 failed), check_t_pool_chunks, test_docs.py, docs_symptoms.py --check, with GAME=01007EF00011E000.
seedds added a commit that referenced this pull request Nov 6, 2026
…etween a raw shader's encode and its command buffer's commit is no longer missing from the table the GPU reads, where its loads read 0 (PR #1427)

An argument block held the address of the raw pool table that was current when it was encoded (gmem_map_gpu). A pool made after that, on another thread while the submission was encoded, was not in that table, though the GPU reads the block only after the commit, as NVN's GPU reads memory when it runs. Raw reads of that pool read 0: the failure that hung agl's task shaders in Ocarina of Time.

The table words are noted as they are encoded (gmem_slot_note) and, at each commit of the command buffer, whole or segment (mtl_sub_commit, sub_cut), pointed at a table of the pools as they are then (gmem_commit_patch). A new table is built when the pools changed, not refilled in place, as earlier segments may be reading the old one. A block's write log follows its table (NvGmlLog), so gml_read decodes its bits against the table the shader searched. The memory report counts the words and those that were out of date (raw pool table words at commit). The idea of rewriting the words at commit is from #1407 (@uncraftablexd).

On the author's stack, loading Ocarina of Time's outdoor save, 5,729 of 154,107 words held an older table by frame 5,100 and 2,582 held the current one behind the pools. Whether this window is behind issue #1326's page faults is open: an address the table misses is skipped, not faulted, and the pools near the M1 Max faults were made 8-41 s before them.

Checked: t_gmem_words 16 checks, 0 failed; t_pool_chunks 102, 0 failed; test_docs, test_gfx_tests_makefile, docs_symptoms --check. Donkey Kong Bananza (raw stores and the write log), hosts at 6d928fd with and without this (the build's libraries are chain cfcc3a82c78b), fresh save into the mining tunnel, 210 s each on an M6: no GPU error on either, the same 4 VP9 red lines, frame p50 16.66 ms on both, 23 long-frame lines each; the patch rewrote 1 of 6,035 words.

Co-authored-by: Pedro Nascimento <91118+lunks@users.noreply.github.com>
@seedds

seedds commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Landed on main as 339d9f2 with your code unchanged, credited with Co-authored-by, and #1407 named for the idea. Thank you.

Checks: t_gmem_words 16 checks and t_pool_chunks 102, 0 failed. Donkey Kong Bananza (raw stores and the write log) A/B on an M6, fresh save into the mining tunnel, 210 s each: no GPU error, the same red lines and frame times with and without it; it rewrote 1 of 6,035 words there.

#1326 stays open: whether this window is behind its page faults is open (a missed address is skipped, not faulted, and the pools near the M1 Max faults were 8-41 s old). mbanusic's M1 Max run with it will tell.

@seedds seedds closed this Nov 6, 2026
Aeonitis pushed a commit to Aeonitis/nxnative that referenced this pull request Nov 6, 2026
…er and shader chain, the branch's host services, movie clock, packing and Ocarina of Time additions

Upstream reached about 60 fps in Kokiri Forest with its own versions of most of the branch's merged PRs, so where both changed the same code upstream's is kept:
- The shader chain is upstream's (translator, lock, dmsh tools, Ryujinx sources), plus the branch's bracket-depth flattening (dmsh_depth.py, test_dmsh_depth.py); chain c732699d4fdb.
  Not carried: the LDSM subgroup fence (seedds#1376, needs a translator rebuild; upstream's HMMA/LDSM rework reports clean movies) and the shared raw-memory lookups (seedds#1405, superseded by upstream's last-entry lookups).
- The renderer (dm/gfx/nvn, dm/gfx/msl) and its tests are upstream's. Not carried: seedds#1390-seedds#1394, seedds#1404, seedds#1408, seedds#1409, seedds#1417, seedds#1418, seedds#1420, seedds#1427, seedds#1320, seedds#1331, seedds#1332, seedds#1368, seedds#1285 renderer parts, pool_skip_retired, and the branch's warning fixes and prewarm progress there. They stay on oot-integration.
- Ocarina of Time's profile is upstream's, with the branch's 12 movie compute rows in pso_manifest.tsv, PLAN.md and PR-AUDIT.md.
- shader_split.py is upstream's plus the BAHS record check (test_split_bahs_records.py).
- Host: upstream's EventFd, PlayReport send, audio lock and vibration answers; the branch's service sessions (hle_sf, hle_prepo), sync waits, AV1 movie clock and parallel decode, diag_abort frame fix and focus control are kept.
- Scripts: shader.noindex, tmp.noindex, notes outside the shader key, bench and regress changes.

Checked: the Mac host builds (GAME=0400486024DEC000); host unit tests pass; the Python tool tests pass on Python 3.13 except test_dmsh_pool_arrays, test_nro_pack, test_nsp_dump and test_nsp_incremental, which fail on upstream main as well. Not run: the full shader build on this chain, gameplay.
Aeonitis pushed a commit to Aeonitis/nxnative that referenced this pull request Nov 7, 2026
…ixes (chain 844bf1a5ea8f, exactly upstream's), raw pool table words at commit (seedds#1427), incremental pool tables (seedds#1489), ocarina input stream (seedds#1527), damaged .zs check (seedds#1530)

Conflicts: chain tools and shader_split.py taken from upstream (dmsh_depth.py dropped: upstream compiles the 259-bracket shaders its own way); the build script keeps shader.noindex and takes upstream's progress watch; docs keep both packing entries.
cfryerdev pushed a commit to cfryerdev/nxnative that referenced this pull request Nov 7, 2026
…a, gmem commit repoint on the test stack

seedds#1589 (carrying seedds#1391 + seedds#1482) the GPU memory guard reads the residency
set's size and stops before macOS runs out of RAM; hand-merged with
seedds#1590's ghost-active alias tables. seedds#1599 soft textures (gated, default
off). seedds#1662's tip commit: the ocarina plays into the Mac's microphone
(DM_MIC=1). seedds#1427 raw pool table words follow the pools at commit, as a
candidate for seedds#1326's page faults (its window is skipped, not faulted:
kept for the record). Full-map shader library (42,848), clearing the
no-stamp and c1_full red lines.

In-game: the GPU page fault at scene changes (seedds#1326) remains on this
stack - one session lost the picture at frame 55,940 with the device
revoked; the fault predates seedds#1585, which was excluded after the M2 Pro
report but is not its cause.

Front-ends: host change only, chain 2027aa29b1b7.
@matijagrcic

Copy link
Copy Markdown

Included this fix in my NX2Apple 1.2.3 so raw pool-table pointers and their write logs stay paired with the current table at commit. With #1331, #1707 and #1708 also included, my roughly 4h play session ended normally with no GPU hang.

Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…etween a raw shader's encode and its command buffer's commit is no longer missing from the table the GPU reads, where its loads read 0 (PR seedds#1427)

An argument block held the address of the raw pool table that was current when it was encoded (gmem_map_gpu). A pool made after that, on another thread while the submission was encoded, was not in that table, though the GPU reads the block only after the commit, as NVN's GPU reads memory when it runs. Raw reads of that pool read 0: the failure that hung agl's task shaders in Ocarina of Time.

The table words are noted as they are encoded (gmem_slot_note) and, at each commit of the command buffer, whole or segment (mtl_sub_commit, sub_cut), pointed at a table of the pools as they are then (gmem_commit_patch). A new table is built when the pools changed, not refilled in place, as earlier segments may be reading the old one. A block's write log follows its table (NvGmlLog), so gml_read decodes its bits against the table the shader searched. The memory report counts the words and those that were out of date (raw pool table words at commit). The idea of rewriting the words at commit is from seedds#1407 (@uncraftablexd).

On the author's stack, loading Ocarina of Time's outdoor save, 5,729 of 154,107 words held an older table by frame 5,100 and 2,582 held the current one behind the pools. Whether this window is behind issue seedds#1326's page faults is open: an address the table misses is skipped, not faulted, and the pools near the M1 Max faults were made 8-41 s before them.

Checked: t_gmem_words 16 checks, 0 failed; t_pool_chunks 102, 0 failed; test_docs, test_gfx_tests_makefile, docs_symptoms --check. Donkey Kong Bananza (raw stores and the write log), hosts at 9fe44cc with and without this (the build's libraries are chain cfcc3a82c78b), fresh save into the mining tunnel, 210 s each on an M6: no GPU error on either, the same 4 VP9 red lines, frame p50 16.66 ms on both, 23 long-frame lines each; the patch rewrote 1 of 6,035 words.

Co-authored-by: Pedro Nascimento <91118+lunks@users.noreply.github.com>
UserDerezzed pushed a commit to UserDerezzed/nxnative that referenced this pull request Nov 12, 2026
…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
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.

3 participants