Skip to content

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

Closed
lunks wants to merge 2 commits into
seedds:mainfrom
lunks:pr/gmem-pool-table
Closed

lunks wants to merge 2 commits into
seedds:mainfrom
lunks:pr/gmem-pool-table

Conversation

@lunks

@lunks lunks commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Ocarina of Time (Switch 2, 0400486024DEC000, v131072; profile in #1299) hangs the GPU, and in longer sessions floors go missing. Reports: #1265 (the red line eight minutes in, floors missing), #1326 (its first hang, kIOGPUCommandBufferCallbackErrorHang with a GPU restart for an MMU interrupt; both of its logs have the red line), #1347 (forcing the old table size gives the hang).

Cause: the raw global memory pool table had a fixed 4,096 entries. The game's save load makes 4,097 to 4,522 pools. The host left the highest ones out ([RED] raw global memory: N more memory pools than the 4095 entries of the pool table) and shaders read 0 for addresses in them. agl's task shader 99280b7b8aae78df (and 47f9b38490dcf9a2) reads both ends of a range through raw loads and loops (min(end - start, 256) - 1) >> 5 times, so with both ends 0 it loops 2^27 - 1 times. DM_NVN_HANGLOG named both in the two segments that hung. The lookup itself stays bounded; the zeros reach the game's arithmetic, so every pool has to stay in the table.

Fix: the host sizes the table to its pools, a power of two of at least 4,096 entries up to 2^20, with the count in entry 0's delta word (gmem_map_gpu). The translator's binary search loops log2(count) times from that count (GlobalToStorage.TryGenerateRawStorageOp). The table is rebuilt when pools come or go, which also shrinks it. The red line for pools still left out gives the first address and the size. The translator is rebuilt from these sources and the chain version changes.

Fixes the hang the overflow caused. Does not cover: #1326's second run, which ended in GPU write faults at 15.5 GB of GPU allocations; nor the hang dacONJA reports on #1326 with the grown table in (4,098 ranges, no overflow line, hang several minutes later, GPU about 32 ms a frame), which is still open; nor the memory growth in #1347.

stan69b's oot-b-wip branch (d7f5b40) has the same design; it has no PR, and they said on #1326 to go ahead with ours. Thanks to them for the comparison. Our comment there: #1326 (comment)

Reference entry: More memory pools than the raw pool table had entries.

Checked

  • Tests: t_pool_chunks (85 checks: 5,000 pools, every address found as the shader finds it, the table is the least again once they go), t_gmem_raw (new, 61 checks: the translated load on the GPU against the host's table, pools past entry 4,095, 0 outside any pool), test_dmsh_ampere_mem.py (new RawGlobal), every test_dmsh_*.py with the rebuilt translator, test_dmsh_abi.py, test_nsp_chain.py, test_docs.py. lint_c.py tidy on nvn_mtl.m and the two tests: no finding above the baseline. The rest of the Metal suite (make -C dm/gfx/tests run) was not run.
  • Games run: Ocarina of Time v131072 on our stack, which carries the same fix in another form (a hint per 2 MiB chunk and a write log with a bit per entry, as our base differs from main). Before: 3 of 8 save loads wedged the GPU and the overflow line was in every run. After: 3 save loads clean and about 15 minutes of play with no hang and no overflow line. This branch itself was not played: its shaders need a full rebuild.
  • Devices and OS: Apple M5 Pro, macOS 27.
  • Chain change: the translator rebuilt from this branch's sources (dotnet publish, the 5 dlls copied, tool_lock.py --update-lock, dmsh_chain.py --header). Every game's shaders are rebuilt; only shaders with raw global memory change their MSL (Donkey Kong Bananza has two), and I did not compare other games' MSL. regress.py was not run.

Docs

  • The finding is in docs/reference/shaders.md and python3 dm/tools/docs_symptoms.py was run (the symptom index)
  • A chain tool changed: dmsh_chain.py --header was run and dm/gfx/nvn/dmsh_chain.h is part of the change
  • No game files, keys or data extracted from a game in the change or the attachments

… were left out, which hung the GPU in Ocarina of Time; chain 7006d127a074

The table had a fixed 4,096 entries. Loading Ocarina of Time's Link's house autosave made 4,097 to 4,522 pools; the host left the highest out, and shaders read 0 there. agl's task shader 99280b7b8aae78df reads both ends of a range through raw loads and loops (min(end - start, 256) - 1) >> 5 times: with both 0, 2^27 - 1 iterations, kIOGPUCommandBufferCallbackErrorHang (DM_NVN_HANGLOG put it and 47f9b38490dcf9a2 in both hung segments). The lookup itself stayed bounded. Upstream seedds#1265 saw the overflow eight minutes into gameplay, with floors missing; seedds#1326's logs have it too, and seedds#1347 reports that forcing the old size gives the hang.

- Host (nvn_mtl.m gmem_map_gpu): a power of two of at least 4,096 entries, as many as the pools need (up to 2^20), the count in entry 0's delta word; freed pools leave at the next rebuild, which shrinks it again. The red line for pools still left out gives the first address and the size.
- Translator (GlobalToStorage.TryGenerateRawStorageOp): the binary search loops log2(count) times from that count. Rebuilt translator, tool lock.
- Cost: 128 KB of table up to 4,095 ranges, 256 KB at 4,522.

Checked: t_pool_chunks (5,000 pools: every address found as the shader finds it, the table shrinks again), t_gmem_raw (the translated load on the GPU against that table, pools past entry 4,095 and an address in no pool), test_dmsh_ampere_mem.py RawGlobal, every test_dmsh_*.py with the new translator, test_dmsh_abi.py, test_nsp_chain.py, test_docs.py, lint_c tidy on the three C files (no new finding). Not run in the game from this branch: it needs the rebuilt shaders.
@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation shader chain The shader translation chain (DMSH_CHAIN): a change rebuilds every game's shaders labels Nov 5, 2026
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 5, 2026
…, replacing A-1

Ports the code of lunks' commit 574a38f (github.com/lunks/nxnative,
fix/gmem-pool-table; seedds#1326, comment 6664549683) and reverts
A-1's code (d7f5b40) in the same change. Its parent ded89d4 is lunks' own
stack, so the commit is not cherry-picked whole: only nvn_mtl.m (gmem_map,
gml_read, gml_take), dmsh_abi.h, GlobalToStorage.cs, t_gmem_raw.m (new),
the tests Makefile, t_pool_chunks.m and test_dmsh_ampere_mem.py. Their game
README, dlls, lock and chain header are left out (rebuilt in A-9).

lunks' design: a power-of-two table of at least 4,096 entries, as many as
the pools need (up to 2^20), the count in entry 0's delta word, shrinking
again when pools are freed; the search loops log2(count) times; a hint is
two words (32-bit chunk number, entry index); the red line names the first
unresolved address. lunks found with DM_NVN_HANGLOG what hung: agl task
shader 99280b7b8aae78df loops (min(end - start, 256) - 1) >> 5 times over
a range whose ends it reads through raw loads, 2^27 - 1 times with both 0.

Adjusted while porting: test_dmsh_ampere_mem.py's RawGlobal used the
storage-slot setup of lunks' own AtomgCas test (from their stack), so it
carries those three instructions itself; its Metal fixture compiles without
-fbracket-depth, as MAC_METAL does here (A-3b). A-1's RawGlobalMemory test
and test_dmsh_abi's pool-table check go with A-1's code; the 0x1000 ratchet
entry comes back with DMSH_GMEM_MAP_ENTRIES.

A-1b's docs are kept, reworded: the reference entry's Fix is lunks' design,
credited to lunks; the game note says so.

Checked: t_pool_chunks 94 checks, t_gmem_raw 61 checks (8,192 entries, 55
probes, on the GPU), test_dmsh_ampere_mem and test_dmsh_abi pass with a
translator built from this source. Not run in the game yet.

Why 574a38f and not lunks' upstream PR seedds#1360: seedds#1360 is the same fix
written for upstream main, whose pool table still has the page-hash write
log and no chunk hint; this branch has seedds#1299's bitmap write log and chunk
hint, and every source file of seedds#1360 conflicts with them. 574a38f is
lunks' own version on seedds#1299's table. Against our A-1 it keeps the same
search and speed, and adds a simpler two-word hint, a table that shrinks
again, the unresolved address in the red line, a GPU test of the translated
lookup, and lunks' in-game runs (3 clean save loads, about 15 minutes of
play). Once seedds#1299 and seedds#1360 land upstream, this should merge with little
conflict.
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 5, 2026
…7c132d

dotnet build -c Release of dmsh_xlate from this branch's source: A-2
(ATOMG.CAS), seedds#1359's float add atomics (A-6), lunks' 574a38f pool table
(A-8), upstream main (A-5). seedds#1359's and lunks' own dlls are not taken.
dmsh_xlate.dll, its pdb and Ryujinx.Graphics.Shader.dll copied into
dmsh_xlate_bin (launcher kept; Ryujinx.Common.dll and Spv.Generator.dll,
whose sources did not change, kept as committed). tool_lock.py
--update-lock (Metal unchanged at 32023.922), dmsh_chain.py --header:
chain a5bef9d6ad60 -> cd1da67c132d.

Checked with this binary: every dm/gfx/tests/test_dmsh_*.py, t_gmem_raw
(61 checks on the GPU), t_pool_chunks (94), test_nsp_chain, test_dmsh_abi,
test_docs, tool_lock.py --check-tools. Not merged: seedds#1357 (-fbracket-depth
on every compile; A-3b keeps the compile cache), seedds#1360 (see A-8).
@PucaVaz

PucaVaz commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Tested in game on a second machine: Apple M4, 24 GB, macOS 27.2, Xcode 27.2, Ocarina of Time v131072 from a NUP. This is the earlier commit 574a38f (fix/gmem-pool-table), not this PR's rebuilt head.

A new game from the title, through the prologue and the owl, into the first 3D scene:

11.3 s   raw global memory: pool table of 2131 ranges in 4096 entries (pool generation 2139), rebuilt whenever a pool comes or goes
396.0 s  raw global memory: the pool table grows to 8192 entries for 4104 ranges   (near frame 13,200)
  • The pools peaked at 4,302, past the old table's 4,095.
  • No more pool ranges line, no command buffer error (Hang, PageFault, SubmissionsIgnored), no FATAL.
  • The run went on for 165 s and about 3,000 frames after the table grew (16,200 frames, 561 s in all). Then my own test harness stopped it, because macOS had reported memory pressure for 120 s (the game was at 9.9 GB resident, 8.9 GB GPU allocated). It was not a hang.

One non-blocking note from reading the diff: the table is still a new buffer on every rebuild, whenever a pool comes or goes, so at about 4,300 pools that is a 256 KB allocation plus a residency commit each time. A small ring of reused table buffers, each reused once its fence has passed, would avoid that churn. It isn't needed for this fix.

Drafted with Claude; reviewed by me.

@PucaVaz

PucaVaz commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

A longer run on the same build (574a38f, M4 24 GB), this time into gameplay: a new game through the opening until Link is controllable in his house, where the game autosaved.

  • The table grew to 8192 entries at 351 s (4,104 ranges), shrank once pools were freed, and grew again at 571 s (4,108 ranges).
  • Memory pools peaked at 4,543 in Link's house.
  • No more pool ranges line, no command buffer error (Hang, PageFault, SubmissionsIgnored), no FATAL.
  • The run ended at 597 s on my test harness's own 12 GB footprint limit, not a hang. In Link's house the game was at 11.6 GB footprint with 10.5 GB GPU allocated, and 12.8 GB at the stop.

Then the save load itself, the case that wedged in your runs. Same build, "Kokiri Forest, Autosave" loaded through Continue → Load, in handheld mode at upscale 2.0:

  • The table grew to 8192 entries for 4,100 ranges at 78.6 s, during the load. Pools went from 3,730 to 4,523, peaking at 4,557 in Link's house.
  • About 130 s of gameplay with walking. No overflow line, no Hang, PageFault or SubmissionsIgnored, no FATAL.
  • The window was closed by hand at 216 s, with a clean exit.

Drafted with Claude; reviewed by me.

@lunks

lunks commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for testing on the M4 24 GB, including Continue → Load of the Kokiri Forest autosave, the case that wedged for us. On the ring of table buffers: I'd rather keep this PR to the table growth and look at the ring as a follow-up if rebuilding the table shows up in a profile.

gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 5, 2026
…tries (Ocarina of Time's GPU hang)

Combined with seedds#1299's lookup hints and entry bitmap write log: the table's
count is in entry 0 (as seedds#1360), hints name only entries below 4,096 (the
rest go by the search), and the write log is as long as the table.
Translator binaries and chain lock to be rebuilt.
gnosticgeek added a commit to gnosticgeek/nxforge that referenced this pull request Nov 5, 2026
…dds#1376 (float add atomics, pool table count, LDSM); chain lock and header regenerated (65ed9f9547f9)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 5, 2026
From upstream f3ac5ea (the Ocarina integration that seedds/nxnative draft PR
seedds#1386 sits on): the game's streaming allocator keeps about 5,100 address regions
in the alias region, spread up to +4.95 GB, and with the profile's 2.38 GB
VammManager::AllocateAddressRegion failed after 5-15 minutes outdoors (nn::diag
abort). mem_alias goes to 8 GB and mem_reserve grows by the same 5.6 GB so the
heap keeps its size. Profile values only; the rest of that commit (pool tables
skipping retired IDs, a 8,191-entry raw global table instead of seedds#1360's growing
one, translator changes) waits for its own upstream pull requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
…nly the pools the game took a buffer address in: every pool was wired for it, which slowed Ocarina of Time's frames to 60-65 ms

Its marks go into the pool table that seedds#1360 sized to the pools (gmem_map here); t_pool_chunks checks the mark, then the chunk hints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
@deanpektas27

Copy link
Copy Markdown

Tested on a third machine, and a note on combining this with #1299, which changes the same table.

Setup: Apple M3 Max, 36 GB, macOS 27.0.1, Xcode 27.2. Ocarina of Time v131072 from a NUP, Mac build (--platform mac), windowed, Continue → Load of the Kokiri Forest autosave. The game runs were on my local integration branch: main plus #1299 and 16 other open Ocarina PRs (#1282, #1284, #1285, #1300, #1312, #1320, #1331, #1332, #1343, #1345, #1353–#1357, #1359).

Before (without this PR): 34 s after the load, on walking out of Link's house, [RED] raw global memory: 13 more memory pools than the 4095 entries of the pool table, then kIOGPUCommandBufferCallbackErrorHang, InnocentVictim, SubmissionsIgnored and the GPU-lost alert. Same as #1326's first run.

After (this PR merged as below): the pool table grows to 8192 entries for 4101 ranges right after the load. A 12-minute session (716 s, 20,334 frames, forest and Link's house) had no hang and no command buffer errors. Pools peaked at 4,660. GPU allocations stayed between 11 and 15 GB without a trend and peaked once at 16.3 GB, with no write faults (#1326's second run faulted at 15.5 GB).

Merging with #1299: the two conflict in gmem_map, GlobalToStorage.TryGenerateRawStorageOp, dmsh_abi.h and the tests. #1299 sizes two more things to 4,096 entries, and both break once the table grows:

  • Chunk hints. Word 6 of slot chunk % 4096 packs a 12-bit entry index with a 20-bit chunk tag, so an entry past 4,095 cannot be named. In the merge, hints stay in the first 4,096 slots, but word 6 holds the whole entry index and word 7 the 32-bit chunk tag (valid for addresses below 2^53; above that the shader searches). The shader takes a hint whose tag matches its chunk; otherwise it runs this PR's search loop from the count in entry 0's word 4.
  • Write-log bitmap. It has DMSH_GMEM_MAP_ENTRIES / 32 words, one bit per entry, so a store through an entry past 4,095 would set a bit outside the log buffer. In the merge, gml_take sizes each log to the table it is taken for (gml_words(map)), releases a free log that is smaller than that, and gml_read walks that many words.

Branch: deanpektas27/nxnative:oot/pr1299-pr1360 is #1299's head merged with this PR's head, with only that resolution on top. It also brings in the 10 main commits #1299 is behind. The translator was rebuilt from that source (.NET SDK 10.0.403), giving chain 02857393f603. The merge commit is the one to review: deanpektas27@7109760

Checked on that branch: test_dmsh_ampere_mem.py (65, including RawGlobal), test_nsp_chain.py, test_xlate_anycpu.py and test_docs.py (31), t_pool_chunks (94), t_gmem_raw (61) and the full make -C dm/gfx/tests run suite all pass. Three new t_pool_chunks checks: a write-log bit for an entry past 4,095 resolves to its pool, the log has one bit per entry of the grown table, and hints in the grown table name the entry the search finds. t_gmem_raw runs the translated shader on the GPU against an 8,192-entry table; its large-pool probe takes the hint path and the small pools take the search. On the integration branch, t_image_usage's "RGBA8 read sees preserved clear bits and the atomic OR" check is flaky with or without these changes (2 of 40 runs failed without them, with a shader build loading the GPU).

This only matters for whichever of #1299 and #1360 merges second; I'm happy to rebase the merge onto that one. #1394, #1404 and #1417 also change gmem_map_gpu and are based on main. Whichever of them lands alongside #1299 needs the same check: no hint index or write-log bit may assume 4,096 entries.

@LucyferDev

Copy link
Copy Markdown

This PR and #1299 (at ee9b776) both change the raw global memory pool lookup, and they don't merge as they stand. Anyone who tests Ocarina of Time on #1299 currently has to leave this fix out. On a 24 GB M4 Pro, that build hit this PR's overflow line at 6:54 into play, hung the GPU at about 9:27, and ended in a kernel panic (report in #1265).

Text conflicts (merging eaa7a1d into #1299's head, both on current main):

  • GlobalToStorage.cs: 3 hunks
  • nvn_mtl.m: 3 hunks
  • dmsh_abi.h, t_pool_chunks.m, test_dmsh_ampere_mem.py: 1 hunk each
  • plus the generated chain files

Design conflict. #1299 adds a 2 MiB chunk hint in front of the binary search, and its encoding assumes a table of exactly 4,096 entries:

  • Host (nvn_mtl.m, gmem map build): hint = chunk * DMSH_GMEM_MAP_ENTRIES + k, stored in word 6 of entry chunk & (DMSH_GMEM_MAP_ENTRIES - 1). So the entry index k gets 12 bits, and the chunk tag sits above them.
  • Shader (GlobalToStorage.cs): indexMask = (1 << GmemMapLog2) - 1 with GmemMapLog2 = 12. The tag check is hint >> GmemMapLog2 == chunk, and the fallback is the fixed 12-step search from 1 << (GmemMapLog2 - 1).

This PR makes the table a power of two from 4,096 up to 2^20 entries, with the count in entry 0's delta low word, and makes the search follow that count. As I read the two together:

  1. The search: [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 fixed 12 steps reach only the first 4,096 entries, so a merge has to take this PR's count-driven loop.
  2. The hint: an entry index of 4,096 or more doesn't fit in the 12-bit field. Its hint then carries a tag that never equals the chunk, so the shader rejects it and falls back to the search. That's correct, but the pools past 4,095 never get a hint. Ocarina has 4,097–4,522 pools after its save load, so the hint stops helping exactly where this PR matters.

A possible combined layout. Words 6 and 7 of an entry are both unused under this PR:

  • word 6: the entry index, up to 20 bits
  • word 7: the chunk tag
  • the hint slot is chunk & (count - 1), with the count from GmemMapCountWord; the host writes the same.

That keeps #1299's NDVE speed-up (title movie 47.6 → 16.67 ms) for any table size. Whether the hint needs to cover more than the first count slots is the authors' call.

I haven't built or played a merged version: the above is from reading both diffs, and from the run in #1265, which has #1299 without this PR. I can test a combined branch on the same Mac if one of you pushes it.

@deanpektas27

Copy link
Copy Markdown

@LucyferDev a combined branch is already up, from my comment above: deanpektas27/nxnative:oot/pr1299-pr1360 (merge commit 7109760). Its parents are the heads you read, #1299 at ee9b776 and this PR at eaa7a1d. It would help if you ran it on your 24 GB M4 Pro, since your #1299-only build is the one that hung at 9:27 and panicked.

It uses your layout with one difference:

On whether the hints should cover count slots, I don't have numbers yet. In my 12-minute session the pools spanned 11 to 16 GB. That is more 2 MiB chunks than 4,096 slots hold (8 GB), so some chunks must lose their hint to another and take the search. Following the count would be a small change: the host writes slot chunk & (cap - 1), and the shader masks with the count it already reads for the search. I can push it to the same branch if the authors prefer it, or first measure how many chunks lose their hint in Ocarina.

#1427 (table words rewritten at commit) touches the same table: it repoints argument blocks at gmem_map_gpu() at each commit. Its new test t_gmem_words looks for a pool in only the first DMSH_GMEM_MAP_ENTRIES entries, so with this PR it would miss a pool past entry 4,095.

lunks added a commit to lunks/nxnative that referenced this pull request Nov 6, 2026
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.
Main's translator changed (8feddfe: F2FP, I2FP, BindlessToArray and others); this branch's GlobalToStorage.cs merges without a text conflict. The translator was rebuilt from the merged source (dotnet publish, .NET 10.0.403), dmsh_chain.lock rewritten by tool_lock.py --update-lock and dmsh_chain.h by dmsh_chain.py --header: chain 6fe832f2db50.

Checked: test_dmsh_ampere_mem (66) with DMSH_TEST_XLATE, test_nsp_chain, test_xlate_anycpu, test_docs, test_dmsh_pool_arrays, docs_symptoms.py --check; t_pool_chunks (85 checks) and t_gmem_raw (61 checks, an 8,192-entry table) with GAME=01007EF00011E000.
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
…ol table grows with the pools: pools past 4,095 were left out and hung the GPU in Ocarina of Time

Translator binaries, lock and chain header kept until the translator is rebuilt on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek added a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
…dds#1300 seedds#1359 seedds#1360; chain lock and header regenerated (c5757270bc73)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
…ss over IDs of another type (Ocarina of Time's residency commits 12 times fewer)

Resolved against NXForge's pool table (seedds#1360's patching, seedds#1393's pool_skip_retired):
- pool_entry passes over an ID whose registration resolved to another texture type (treg_t.tt) after the gone check, so both the full walk and the patch path skip it
- the full build and the patch path take their buffers from the spares (pool_buf) and retire the replaced one (pool_buf_retire); pool_snap keeps its sub field
- the pool table log line keeps NXForge's why/patched/kept counts and adds made/reused/passed over
- t_int_float_view.m keeps main's checks; the Makefile has t_gmem_raw and t_pool_table
- t_pool_table marks the 2D table out of date before its second rebuild, since a patch on registrations alone would not walk the 3D ID

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
… texture object holds each shared texture in one lookup (Ocarina of Time's cutscene texture creation 2.4 times cheaper)

Resolved against NXForge's serial table (seedds#1360's program generation, seedds#1300's g_ghost_active):
- serial_put / serial_del keep the program generation and add seedds#1439's held counts
- tex_put keeps the same-object check and the g_ghost_active ring, and counts g_by_serial's names as seedds#1439 does
- t_int_float_view.m keeps main's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
@seedds

seedds commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Thank you, and thanks to everyone whose reports fed into it. The raw pool table overflow is fixed on main (landed on main in b1d7c70 and 6121fd7), so I'm closing this. Final checks on the rebased tree (full shader build, walks, regression) are still running, so please report anything you see.

@seedds seedds closed this Nov 6, 2026
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
) replaces the pull request heads

Upstream landed Ocarina of Time in main (chain adec7d81d970: "holds 60 fps in
Kokiri Forest, 4 GB less") and closed the pull requests it folded in, with
reworded and partly different code. Mixing a pull request head with its landed
version hunk by hunk silently broke things (two GlobalToStorage tables, a
duplicate AF2ip, two mtk_pso definitions), so the rule here is one version per
feature:

- Pull requests upstream closed (seedds#1299, seedds#1300, seedds#1312, seedds#1343, seedds#1345, seedds#1353,
  seedds#1354, seedds#1359, seedds#1360, seedds#1373, seedds#1428, seedds#1430, seedds#1434): their hunks are taken out
  of our side before the three-way merge, so upstream's landed version stands
  alone. Gone with them: bindless pool table patching (reg_bump), the ghost
  table's per-submission hold, the growing raw pool table and its write-log
  sizing, seedds#1428's pool_buf spares (upstream's pool_table_take is the same
  idea), the ixc-based indirect mesh draw (upstream uses mdx), and their tests
  (t_pool_table, t_mesh_indirect, t_gmem_raw, finalized_bindless_pool,
  test_bnsh_nvn2, test_ndve).
- Open pull requests that upstream's landing supersedes: seedds#1394 (upstream's
  mtl_pool_addressed) and seedds#1393 pool_skip_retired (upstream's budgeted pool
  walk; the profile does not use it) are dropped with their tests and docs.
  seedds#1368's mesh pipeline prewarm is dropped too: upstream kept the background
  build in nvn_geometry.inc, and the prewarm's mtk_build / mtk_claim /
  g_warm_mps went with it.
- Kept, as upstream does not have them: NXForge's own features (GPU watchdog,
  present filters and anti-aliasing, aniso floor, quick settings, pipeline
  archive compaction, upload breakdown, memory report lines) and the still
  open pull requests seedds#1304, seedds#1305, seedds#1308, seedds#1320, seedds#1331, seedds#1332, seedds#1355, seedds#1356,
  seedds#1369, seedds#1376, seedds#1377, seedds#1379, seedds#1382, seedds#1390, seedds#1391, seedds#1392, seedds#1414, seedds#1420,
  seedds#1436, seedds#1439, seedds#1446, seedds#1453.
- Combined where both sides changed the same lines: GW_N 1 << 16 with seedds#1320's
  wide-write intervals (GW_PAGES = SBW_WPAGES, 128 slots), the CPU upload log
  with upstream's linear upload route, arch_open_end before the compaction
  limit, cbn_prog in geo_gs_one (seedds#1382).
- Translator: upstream's sources and binaries, plus seedds#1414's
  InstEmitAmpereMem diff and the LDSM SubgroupBarrier in
  InstEmitAmpereTensor.cs. The binaries and dmsh_chain.lock are upstream's
  until the Mac rebuilds them (dotnet publish, tool_lock --update-lock,
  dmsh_chain --header); dmsh_chain.h is regenerated for now.
- RED_ONCE keys: upstream took 153 and 154, so the present anti-aliasing lines
  move to 156 and 157 and seedds#1420's packaged-entry line to 158 (array 159).
- Host: hle_net.c and t_net_eventfd.c are upstream's (its eventfd poll waits
  out the whole timeout, which our 5d7920c fixed another way).
- Kept ours: dm/tools/romfs_image.py and its test, so the 26 GB RomFS image is
  not packed again.

dm/tools tests on Linux: same failures as on 875449e (Linux-only quirks),
plus test_nlg4 and test_dmsh_pool_arrays, which fail on upstream main too.
Not yet built on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
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) shader chain The shader translation chain (DMSH_CHAIN): a change rebuilds every game's shaders

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants