Repository navigation
Splatoon Raiders (0400380020056000) holds its frame rate at the first boss's third stage: a submission's pending-write list holds 32,768 writes (past 8,192 it counted as writing everywhere) - #1435
Conversation
…counted as writing everywhere, and Splatoon Raiders' third boss stage fell to 1-2 fps The stage queues about 11,000 buffer writes in one submission (7 inline uniform updates before each of about 1,550 ink-map occlusion queries; 2,800 in the game's other fights). Past PEND_MAX the submission counted as a write anywhere (g_pend_over), gw_commit carried that past it (g_gw_all), and every partly covered uniform shadow was refused while it ran: each query ended its render pass on the 6400x6400 ink map (about 1,000 passes a frame, GPU 420-600 ms on a 16 GB M2 Air), the GPU fell behind, the submissions kept running, and the refusals went on. A pass dump with pass-end call sites named draw_ -> ubd_conflict; the host's counters showed 173,452 refusals, 161,812 of them "earlier submission running". PEND_MAX 32,768 (under 65,536: g_pend_upd is a 16-bit index + 1), PEND_NODES 65,536, and 8,192 hash buckets (the top 13 bits). About 1.9 MB of static tables instead of 0.5 MB. t_ubp_running queues the stage's 10,850 updates: it fails 3 checks before and passes after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ass (E <pass> pass ends in <function>:<line>) Splatoon Raiders' third boss stage opens about 1,500 one-draw passes a frame on its 6400x6400 ink map, and its pass dump had no record at the point where each ended: the rule had to be found by reading the code. rp_end becomes a macro over rp_end_at(__func__, __LINE__), which writes the record when DM_NVN_PASSDUMP is on (no cost otherwise). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tage: the reference entry and the game's note The stage's 11,010 queued writes in one submission overflowed the host's 8,192-entry list. From then on every partly covered uniform shadow was refused, and each of about 1,550 ink-map queries a frame ended its render pass (1-2 fps). Played on a 16 GB MacBook Air M2, docked: before, up to 1,025 passes and 173,452 refusals; after, a median of 17.7-19.9 ms, at most 118 passes and none refused. The reference entry is in rendering.md, and the symptom index is updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s its frame rate at the first boss's third stage: a submission's pending-write list holds 32,768 writes (past 8,192 it counted as writing everywhere)
…n Kokiri Forest, about 4 GB less memory) beside this tree's own work Kept from this tree: the growing raw pool table with two-word hints (plus upstream's read-before-OR on its write log), seedds#1439's held count, seedds#1436's diagnostics, seedds#1447's texture release, seedds#1435, the idle evictions, the GPU watchdog, range-tracked GPU writes, the packaged-texture cache (g_pkg_alias), the synchronous task / mesh pipeline build with prewarm, this tree's F2IP. Taken from upstream: the pool tables (only what shaders read is made, within a budget; pool draws count as depth readers only when one was seen reading, replacing seedds#1430's rule; table reuse), sub_cut's busy rule, the indirect task / mesh draw through mdx, the empty mesh fragment, the larger GPU-write table (65,536 slots), linear textures by rows, the atomic alias on any 32-bit format, its eventfds (with this tree's select and fcntl on them), its prepo Send and audio update lock, its shader split and Ocarina profile and tables. Duplicates both sides added were removed (SDK 22 answers, F2IP, test stubs); RED_ONCE 153 split; t_pool_table (the replaced pool table code) removed; tests adapted to the taken rules. Translator rebuilt; chain 695a9393d125. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers' first boss fight holds its frame rate at the third stage (PR #1435) The stage measures its ink map with about 1,550 occlusion queries a frame, 7 inline uniform updates before each: 11,010 queued writes in one submission (2,800 in the game's other fights). The list held 8,192. Past that the submission counted as a write anywhere (g_pend_over), gw_commit carried that past it (g_gw_all), and every partly covered uniform shadow was refused while it ran: each query ended its render pass on the 6400x6400 map, the GPU fell behind, the submissions kept running, and the stage stayed at 1-2 fps (up to 1,025 passes and 173,452 refusals a run on a 16 GB M2 Air; after, a median of 17.7-19.9 ms, at most 118 passes, none refused). The limit had been raised once before (256 -> 8,192, Monster Hunter Rise), so the list no longer has one: its entries and page nodes grow in pend_room and keep their size for later submissions, g_pend_upd is 32-bit, and g_ubp_idx is sized to the nodes plus the wide list (the old PEND_MAX + PEND_WIDE was below what the nodes allowed). A write anywhere is left for a list that cannot grow and a full wide list. The hash has 8,192 buckets. A pass dump names the host function and line that ended each render pass (E <pass> pass ends in <function>:<line>): rp_end is a macro over rp_end_at, which writes only while DM_NVN_PASSDUMP is on. One run named draw_ -> ubd_conflict. t_ubp_running queues the stage's 10,850 updates (3 checks fail with the old limit), then 43,400. regress.py check on Octopath Traveler II: PASS (59.8 fps both, no new red lines, shaders and MSL unchanged). Co-authored-by: David Padron-Harris <34929160+dpadr@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Landed as 6d5ed71, with you as co-author. Thanks for the clear diagnosis and the before/after runs. One change from the PR: instead of raising the fixed limit to 32,768, the list no longer has a limit. Its entries and page nodes grow when a submission needs more room ( Kept as you wrote them: the test (now extended to 43,400 updates, past what a 32,768 limit holds), the reference entry, the game note and the Checked here: |
…ds, PR seedds#1435; test link fixes) into switch2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SzykpcKm2eZPvPjGPKx1C
…ers' first boss fight holds its frame rate at the third stage (PR seedds#1435)
…ers' first boss fight holds its frame rate at the third stage (PR seedds#1435) The stage measures its ink map with about 1,550 occlusion queries a frame, 7 inline uniform updates before each: 11,010 queued writes in one submission (2,800 in the game's other fights). The list held 8,192. Past that the submission counted as a write anywhere (g_pend_over), gw_commit carried that past it (g_gw_all), and every partly covered uniform shadow was refused while it ran: each query ended its render pass on the 6400x6400 map, the GPU fell behind, the submissions kept running, and the stage stayed at 1-2 fps (up to 1,025 passes and 173,452 refusals a run on a 16 GB M2 Air; after, a median of 17.7-19.9 ms, at most 118 passes, none refused). The limit had been raised once before (256 -> 8,192, Monster Hunter Rise), so the list no longer has one: its entries and page nodes grow in pend_room and keep their size for later submissions, g_pend_upd is 32-bit, and g_ubp_idx is sized to the nodes plus the wide list (the old PEND_MAX + PEND_WIDE was below what the nodes allowed). A write anywhere is left for a list that cannot grow and a full wide list. The hash has 8,192 buckets. A pass dump names the host function and line that ended each render pass (E <pass> pass ends in <function>:<line>): rp_end is a macro over rp_end_at, which writes only while DM_NVN_PASSDUMP is on. One run named draw_ -> ubd_conflict. t_ubp_running queues the stage's 10,850 updates (3 checks fail with the old limit), then 43,400. regress.py check on Octopath Traveler II: PASS (59.8 fps both, no new red lines, shaders and MSL unchanged). Co-authored-by: David Padron-Harris <34929160+dpadr@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What and why
At the third stage of Splatoon Raiders' first boss fight (
0400380020056000), the game dropped to 1-2 fps and stayed there until the stage ended. The GPU was busy 420-600 ms a frame, with up to 1,025 render passes a frame, against about 100 in the rest of the fight.Cause. The stage measures its ink map with about 1,550 occlusion queries a frame. Before each query it makes 7 inline uniform updates, which comes to 11,010 queued writes in one submission (about 2,800 in the game's other fights). The host's list of a submission's queued writes (
pend_add) held 8,192 entries. Past that limit:g_pend_over).gw_commitcarried that to later submissions while it ran (g_gw_all).Change.
PEND_MAX8,192 → 32,768. It stays under 65,536 becauseg_pend_updis a 16-bit index + 1, which a static assert now checks.PEND_NODES16,384 → 65,536, and 2,048 → 8,192 hash buckets.Diagnostic. A pass dump now names the host function and line that ended each render pass:
E <pass> pass ends in <function>:<line>, through arp_end()macro overrp_end_at(__func__, __LINE__). It only writes whileDM_NVN_PASSDUMPis on. Without it, the dump had no record at the point where these passes ended. With it, one run nameddraw_→ubd_conflict.The reference entry is A submission past 8,192 queued writes counted as writing everywhere. The game note's Rendering section and its "Found on this game" list link to it.
Checked
Tests:
make -C dm/gfx/tests GAME=0400380020056000 check_t_ubp_running: a new case queues 10,850 updates in one submission. It fails 3 checks onmainand passes all 11 here.check_t_geometry(1,920 checks) andcheck_t_geometry_hazards(61), which setg_pend_over, pass.test_tbdr_budget.py,test_docs.pyandtest_env_switches.pypass.Games run: Splatoon Raiders
0400380020056000, played docked on the Mac through the first boss fight's third stage. The before and after builds differ only by this change: both are main with PRAGMATA's opening load fits an 8 GB Mac's GPU working set: large-pool chunks retire, pools from 128 MB are chunked, and DM_NVN_TEX_MIP_SKIP #1319 merged in and its memory switches on (DM_NVN_TEX_MIP_SKIP=1,DM_NVN_GMEM_RES=0), withDM_NVN_GPU_TS=segment. The "after" run also had the pass-end diagnostic.earlier submission running, 11,640unreadable)The system's swap did not change across the drop: it looked like memory pressure but was not.
Devices and OS: MacBook Air M2 (Mac14,2, 10-core GPU), 16 GB, macOS 27.0.
regress.py impact origin/main..fix/pend-list-capacity:nvn_mtl.mreaches every NVN game. Of the profiles with a usage manifest: Breath of the Wild, Pokémon Scarlet and Tears of the Kingdom are affected, and Hades is broad (present path only).Not checked yet:
scripts/regress.py check --gameson those games, which are not on this Mac. The change only raises a limit, so behaviour below 8,192 writes a submission is unchanged. A game that passed the old limit now keeps its shadows instead of ending passes.Docs
docs/reference/rendering.mdentry, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index)🤖 Generated with Claude Code