Repository navigation
Conversation
|
Same symptom here on main As an adult, the loads around Kakariko's graveyard, Lake Hylia and the Kakariko gate from Hyrule Field dropped to about 5 fps for seconds. In a 41-minute session, 6 windows of 300 frames ran under 20 fps (lowest 8.7 fps), with single frames up to 910 ms, at about 16, 31 and 47 minutes. From the log:
I have not run this PR yet. Edited: the build was |
…ses drawn as one, PR seedds#2344, on-screen intervals Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
Measured on Ocarina of Time v131072, an M2 MacBook Air (16 GB), handheld at 1440x810, MetalFX, hold off. The host is our cut8 build (main ccd834e plus this PR at head 4ec0c1c). The route is a scripted Kokiri Forest walk from a save, with back-to-back runs and the same keys.
We run with DM_NVN_SEEN_KEEP=0 for now. |
A read mark (pool_seen) stayed with its ID through every new texture (PR seedds#2022) and was cleared only when the ID was emptied, so the marks grew with every area passed through. An hour into Ocarina of Time (0400486024DEC000, v131072) the pool walk line counted 19,201 of its 33,024 IDs as read (22.7 GB of textures), and a walk makes a marked ID's texture whatever its room (pool_entry, under POOL_REQ_BYTES, 128 MB a frame), so each area load, which streams thousands of textures into IDs earlier areas had read, had them made as fast as the read budget allowed: the loads into Hyrule Field and the passages around it froze in runs of 200-670 ms frames for seconds, with 100-300 first texture uploads a frame and GPU allocated at 9.8 GB against 3.9 GB in the session's first minutes. Another player saw the same on an M4 Pro (24 GB): loads around Kakariko and Lake Hylia at about 5 fps, 17,139 IDs marked (14.2 GB) after 41 minutes (PR seedds#2344). The host now keeps the size of the marked IDs' textures (seen_size, g_seen_kb_all). While it is above a quarter of Metal's working set (SEEN_CAP_DIV; about 2.7 GB on a 16 GB Mac, 9 GB on a 48 GB one), pool_seen_scan takes the GPU's marks back every 60 frames (SEEN_REARM; an exchange, so a mark set meanwhile stays for the next scan) and notes the frame each mark was last found in (g_seen_at, up to 60 frames after the read); the shaders set them again at their next read (dm_pool_mark is unchanged, so the chain is not). mtl_reg_tex then keeps the mark for the ID's next texture only when the ID was read within the last DM_NVN_SEEN_KEEP frames (600 by default): the streamed replacement of a material on screen keeps it as PR seedds#2022 needs, and a texture streamed into an ID left behind holds its stand-in until it is read (pool_stand). Below the cap nothing is taken back or dropped, as before: a mark makes the ID's next texture when it is registered, often hundreds of frames before its first read, and the first form of this change, which aged marks whatever their size, slowed a scripted walk out of Kokiri Forest on an M2 MacBook Air (16 GB) to 70-110 ms frames (GPU 44-60 ms) against 34 ms with DM_NVN_SEEN_KEEP=0 (dpadr's runs, PR seedds#2344). The marks are scanned in a pass of their own before the walk, which may stop at the read budget. A stale 1 read by work still running just after a take-back skips one mark, which a read after the next take-back sets. DM_NVN_SEEN_KEEP=0 keeps every mark until its ID is emptied, as before; the pool stand-ins line counts the marks kept and dropped, with the cap and the marked size. The reference entry (Area loads froze late in a session as read marks piled up) is new, the entry for PR seedds#2022 and Ocarina's note link it. Checked: t_pool_seen_replace (67 checks, 15 new: a read notes its frame and the ID counts its texture's size; below the cap the GPU's mark stays and an unread ID keeps its mark at its new texture's size; above it the GPU's mark is taken back while the host's stays, a mark not read within the window goes with the next texture, an ID read again keeps its mark; the size goes with the marks; 50 with DM_NVN_SEEN_KEEP=0), t_pool_standin, t_pool_notes, t_pool_chunks, make run in dm/gfx/tests on 713ac08 plus this (every suite passes), test_docs, env_switches.py --check. Two sessions of play on an M5 Pro (48 GB, macOS 26.6.2) with the first form (no cap), not on the same route: without the change, the runs of 200-670 ms frames above after the first hour and tens of 300-frame windows with a p95 above 40 ms; with it, 78 minutes, one such window in 903, GPU allocated 7.2-9.6 GB from the 6th minute on, following the area rather than the time played, and the area loads single frames of 102 and 155 ms (658 and 248 first uploads). 92,703 new textures kept their ID's mark and 46,614 dropped it; stand-ins made 1,235,328 in 6.19 s over the session against about 128,000 without the change (23 us of the submit thread a frame on average). Not run: any game with the cap (neither the long session nor the Kokiri walk), an A/B on one route, regress.py check (no other game here streams into a bindless pool this way), another game, lint_c.py (no LLVM clang-tidy here), iOS. Open: whether a quarter of the working set is the right cap; whether the area-load frames of 100-155 ms left come from the stand-ins made in them or from the first uploads. Co-authored-by: Cursor <cursoragent@cursor.com>
4ec0c1c to
b5ad456
Compare
|
Thanks for the runs. Rebased on 713ac08 and changed in b5ad456:
|
What and why
About an hour into an Ocarina of Time (0400486024DEC000, v131072) session, the loads into Hyrule Field and the passages around it froze in runs of 200-670 ms frames for seconds, where the same loads were smooth in the first minutes. The
[longframe]lines showed 100-300 first texture uploads a frame, and GPU allocated was at 9.8 GB against 3.9 GB early in the session. Sennbon-99 saw the same on an M4 Pro (24 GB, comment below): loads around Kakariko and Lake Hylia at about 5 fps, with 17,139 IDs marked (14.2 GB) after 41 minutes.Cause: a texture ID's read mark (
pool_seen) stays with the ID through every new texture (PR #2022) and was cleared only when the ID was emptied, so the marks grew with every area passed through. An hour in, thepool walkline counted 19,201 of 33,024 IDs as read (22.7 GB of textures). A walk makes a marked ID's texture whatever its room (pool_entry, underPOOL_REQ_BYTES), so an area load, which streams thousands of textures into IDs that earlier areas had read, had them made as fast as the read budget allowed.Change (host only,
nvn_mtl.m; the shaders'dm_pool_markand the chain are unchanged):seen_size,g_seen_kb_all).SEEN_CAP_DIV: about 2.7 GB on a 16 GB Mac, 9 GB on a 48 GB one) nothing changes from main.pool_seen_scantakes the GPU's marks back every 60 frames (an atomic exchange) and notes the frame each mark was last found in, andmtl_reg_texkeeps the mark for the ID's next texture only when the ID was read within the lastDM_NVN_SEEN_KEEPframes (600 by default). The streamed replacement of a material on screen keeps it, as PR Ocarina of Time: large models no longer go black for one frame twice, 4–5 frames apart, when the game streams new textures into them (a texture ID a shader has read keeps its read mark when it gets another texture) #2022 needs; a texture streamed into an ID left behind holds its stand-in (pool_stand) until it is read.DM_NVN_SEEN_KEEP=0restores the old behaviour. Thepool stand-inslog line counts the marks kept and dropped, with the cap and the marked size.Updated (rebased on 713ac08): the first version aged marks whatever their size. dpadr measured that on an M2 MacBook Air (16 GB): a scripted walk out of Kokiri Forest ran 70-110 ms frames with the default against 34 ms with
DM_NVN_SEEN_KEEP=0. A mark makes the ID's next texture when it is registered, often hundreds of frames before its first read, so dropping marks of the area just left probably moved that work into the frame the next area appears. The cap keeps those hints while they are small and ages them only once they have piled up.Reference entry: Area loads froze late in a session as read marks piled up; the PR #2022 entry and Ocarina's note link it.
Checked
make -C dm/gfx/tests GAME=0400486024DEC000 runon 713ac08 plus this, on an Apple-silicon Mac: every suite passes.t_pool_seen_replace: 67 checks, 15 new (a read notes its frame and the ID counts its texture's size; below the cap the GPU's mark stays and an unread ID keeps its mark; above it the GPU's mark is taken back while the host's stays, a mark not read within the window goes with the next texture, an ID read again keeps its mark; the size goes with the marks), 50 withDM_NVN_SEEN_KEEP=0.t_pool_standin,t_pool_notes,t_pool_chunks,test_docs,env_switches.py --checkpass.regress.py check: not run. No other local game streams into a bindless pool this way, and the reach is the pool walk's read marks.lint_c.py(no LLVM clang-tidy here; CI runs it), iOS.Docs
docs/reference/<subsystem>.mdentry, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index)