Repository navigation
A pool table takes again only the IDs a change reaches instead of being walked whole: registrations, memory pool changes, retired IDs at a new submission and storage takeovers each leave a note - #1489
Conversation
…ng walked whole: registrations, memory pools added or removed, retired IDs at a new submission and storage takeovers each leave a note pool_gpu walked a table of handles traced to no slot whole at any change of g_reg_gen or g_ser_gen, though each change reaches only a few of its IDs. On main (6121fd7), TheekshanaA measured about 53 s of a seven-minute walk through Ocarina of Time's Kokiri Forest spent on 61,000 such builds of its 33,025-entry texture table (PR seedds#1405, seedds#1464). The game registers something between most of its draws, its streamed textures map and unmap virtual pool runs (vrun_set) dozens of times a second, finalized descriptors stay registered, and textures take over each other's storage (alias_take_over, about 8,400 times in six minutes), which bumped g_ser_gen with no note and so also sent every texture ID down the slow path. This ports TheekshanaA's branch oot-pool-table-notes (9579768, 9d486f8) to main's walk. Those commits sat on the table patching of seedds#1299 / seedds#1387, which main did not take, so the patching comes with them: - A registration records its generation and ID (g_reg_log), a storage change its serial (g_ser_log, beside the notes ser_sync takes). A table built at an earlier generation takes again only the IDs they name, by the walk's own rules (pool_entry), and is kept when no entry changes, else copied and patched. - A memory pool added or removed records REG_POOL and no ID: a table takes again only the IDs it found without storage (pool_snap.dead), and treg_gone notes each ID of a finalized pool. A note's generation is stored last (release) and read first (acquire), as pool changes come from threads that do not hold g_rmu. - A table keeps the IDs whose entries are views of retired registrations (pool_snap.trans and .sub); the next submission takes only those again. - A takeover records a note that names no texture (ser_note(0)). - Past POOL_IDS_MAX IDs on a list, a note the ring has overwritten, a bump without a note, a pool that grew or a sparse mapping, the table is built whole as before. The log line says why tables were built whole and how many were patched or kept; the power lines gain "[nvn] pool tables up to frame ...". What main's walk changes on the way: - A patch makes textures only within what the frame may still make (pool_room, taken out of pool_walk_limit), as a walk does. A texture left out counts in the table's left, and the walk goes on next frame as before. - Patched tables take and give their buffers through pool_table_take / pool_table_give, as walked ones do. - A retired registration's view that the read scan writes in place (pool_seen_scan) is listed as transient too, and a table holding such views from an earlier submission does not stand under POOL_SUB_MS. Before, nothing made a table take those IDs again after the submission whose end drops the views. - alias_sweep, and seedds#1387's exception that kept a retired view of a shared texture past its submission, are not needed: main drops every retired view at the submission's end, so the next submission takes those IDs again. - A sampler table follows registrations alone. Checked: t_pool_notes (new, 67 checks). A pool elsewhere keeps the table, the pool that brings an ID's storage patches it, a finalized pool's ID holds the dummy, a bump without a note builds it whole, a retired ID and a takeover are taken again at the next submission without a build, a registration past the budget is left out by a patch, and a read written in place is taken again; patched tables match tables built whole. Without the takeover note, the retake at a new submission or the in-place record, it fails (2, 4 and 2 checks). make -C dm/gfx/tests GAME=0400486024DEC000 for the 35 tests that reach registrations, pool tables, serial notes and the sharing table (t_pool_chunks, t_feedback, t_sparse_tiles, t_image_alias and t_nvn among them): all pass. The Mac host builds, and the two changed files compile for the iPhone SDK. clang-tidy (LLVM 22, not CI's 23) finds nothing new in them. Ocarina of Time, four scripted runs on an M4 with 24 GB, alternating main (88c7e03) and this change, from the autosave at Link's house (180 s in the house, Saria's greeting, 300 s on the balcony): the table was built whole 44,000 and 24,000 times per run on main (67 s and 72 s), about 100 times with this change (about 0.2 s), while about 86,000 tables were patched and 63,000 kept. The submit thread's CPU time per frame (p50) went from 12-15 ms to 3-5 ms; frame p50 is the GPU's and did not move, p99 in the house went from 34.6 and 41.0 ms to 24.2 and 25.6 ms, and frames over 50 ms walking from 23 and 48 to 3 and 2. Same picture, footprint and red lines. Co-authored-by: TheekshanaA <10327965+TheekshanaA@users.noreply.github.com>
|
Thanks for porting this onto main. One question about Is that last part guaranteed? What I would like to be sure of is that no live registered ID keeps a cached view of the evicted texture until the next sync, which would sample the old texture over storage the new one now owns. If |
…ture: the comment says what holds that, and t_pool_notes checks a live sRGB / linear twin alias_take_over skips an entry a live serial holds (serial_holds on g_by_serial) and only unrefs one a finalized serial holds through g_ghost, so no serial resolves differently. The comment said only a retired ID could still hold a view of an evicted texture; a live sRGB / linear twin can too, as its serial holds a view of the shared texture rather than the texture. tex_mtl_ finds such a view stale once its parent has left the sharing table and drops it (serial_del), which notes that serial, so the patch takes the twin's ID again. t_pool_notes gains that case (81 checks, 0 failed): ID 8 is the sRGB twin of a texture whose ID was finalized; a texture of another description takes the storage over and evicts the shared texture; in that submission the twin's view is dropped and made again, no table is built whole, no ID goes down the slow path, and the patched table matches a table built whole.
|
Thanks, good question, and the comment did overstate it. What holds it is that "Only a retired ID can still hold a view of it" was not exact, though: a live sRGB / linear twin ( e4cc4bb rewords the comment to say this and adds the case to |
# Conflicts: # dm/gfx/nvn/nvn_mtl.m # dm/gfx/tests/Makefile
…ng walked whole: registrations, memory pool changes, retired IDs at a new submission and storage takeovers each leave a note; Ocarina of Time's village walk builds its 33,025-entry table 16 times instead of over 4,000 (PR #1489) pool_gpu walked a table of handles traced to no slot whole at any change of g_reg_gen or g_ser_gen, though each change reaches only a few of its IDs. Ocarina of Time registers something between most of its draws, its streamed textures map and unmap virtual pool runs dozens of times a second, finalized descriptors stay registered, and textures take over each other's storage (alias_take_over), which bumped g_ser_gen with no note and so also sent every texture ID down the slow path. TheekshanaA measured about 53 s of a seven-minute walk through Kokiri Forest spent on 61,000 such builds (PR #1405, #1464). This is TheekshanaA's branch oot-pool-table-notes, ported to main's walk by PucaVaz, with the table patching of #1299 / #1387 it sat on: - A registration records its generation and ID (g_reg_log), a storage change its serial (g_ser_log, beside the notes ser_sync takes). A table built at an earlier generation takes again only the IDs they name, by the walk's own rules (pool_entry), and is kept when no entry changes, else copied and patched. - A memory pool added or removed records REG_POOL and no ID: a table takes again only the IDs it found without storage (pool_snap.dead); treg_gone notes each ID of a finalized pool. - A table keeps the IDs whose entries are views of retired registrations, those the read scan writes in place among them (pool_snap.trans); the next submission takes only those again, and such a table does not stand under POOL_SUB_MS. - A takeover records a note that names no texture (ser_note(0)): it changes no serial's texture (an entry a live serial holds is not evicted, serial_table_holds; one a finalized serial holds is only unref'd). A live sRGB / linear twin's view of an evicted texture is found stale by tex_mtl_ and dropped (serial_del), a note naming it. - A patch makes textures only within what the frame may still make (pool_room). Past POOL_IDS_MAX IDs, a note the ring has overwritten, a bump without a note, a resized table or a sparse mapping, the table is built whole as before. The log line says why; the power lines gain "[nvn] pool tables up to frame ...". On landing: the comment and the reference entry name serial_table_holds (#1439 replaced the serial table walk) and describe the sRGB / linear twin case; a registration note is read as a sequence lock (reg_read: its generation is checked before and after its ID, and the writer clears it first), as memory pool changes write notes from threads that do not hold g_rmu and a note the ring overwrote during the read could be taken torn. Checked: t_pool_notes 81 checks (patched tables compared with tables built whole), t_pool_chunks 102, t_alias_takeover 28, t_gmem_words 16, t_feedback 159, t_image_alias 329, t_sparse_tiles 4,253, t_cpu_tex --metal 68, all 0 failed; test_docs; the Mac host builds and the iOS compile check passes (261 files). Ocarina of Time on an M-series Mac, hosts at 51cc8bc with and without this (main's chain has no libraries on disk for it), the village walk from save_ref: tables built whole 4,000+ (5.85 s+) on the base, 12 and 16 (0.11 s, 0.12 s) with this; submit p50 in the village 11-16 ms on the base, about 7-12 ms with this; the same red lines and no GPU error. Vertical stripes in both sides' shots at the same balcony points come from that old-chain setup, not this change. The author measured submit p50 12-15 -> 3-5 ms and frames over 50 ms walking 23 and 48 -> 3 and 2 (M4, two runs per side). No second game: none local has libraries for main's chain, and the one whose regress runs reach these tables (Paper Mario: The Thousand-Year Door) needs 11,861 shaders translated first. Co-authored-by: PucaVaz <65235669+PucaVaz@users.noreply.github.com> Co-authored-by: TheekshanaA <10327965+TheekshanaA@users.noreply.github.com>
|
Landed on main as 0c7e88b, with you and TheekshanaA as co-authors. Thanks for the port and the careful answer on the takeover note. Changes on landing:
Checked on main's tree: |
|
On lunks's question (does any live registered ID keep a cached view of the evicted texture until the next sync): we carry #1439's idea on top of this PR and it closes exactly the gap PucaVaz names. Instead of Checked on main bd9f422 with #1489 applied first: |
…et the dummy (pool_skip_retired): pool tables are kept or patched instead of built whole, about 3 builds a frame walking through Ocarina of Time's Hyrule Castle Town (from PR seedds#1489's idea)
…et the dummy (pool_skip_retired): pool tables are kept or patched instead of built whole, about 3 builds a frame walking through Ocarina of Time's Hyrule Castle Town (from PR seedds#1489's idea) (cherry picked from commit 054538f)
…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.
- Reconcile upstream PRs (seedds#1455, seedds#1447, seedds#1394, seedds#1439, seedds#1489, seedds#1590, seedds#1663) and commits (ffebfd5, e5f1ade) against local fork - Confirm PRs seedds#1455, seedds#1447, seedds#1394, and seedds#1439 are already implemented in ad99858 - Establish that host-only renderer optimizations require zero shader rebuilds and reuse the 42,854-entry b-dc815e0f38ca.img library - Establish that native subword stores (ffebfd5) affect only 515 shaders and leverage cas.db compilation caching - Rank candidates for base M3 Normal + Handheld (>=40 FPS target, ideal 60 FPS) and identify the top three candidates - Recommend the smallest independently verifiable first optimization (host-only PSO archive epoch separation from PR seedds#1590) - Update documentation index, plan, changelog, and known issues
…ng walked whole: registrations, memory pool changes, retired IDs at a new submission and storage takeovers each leave a note; Ocarina of Time's village walk builds its 33,025-entry table 16 times instead of over 4,000 (PR seedds#1489) pool_gpu walked a table of handles traced to no slot whole at any change of g_reg_gen or g_ser_gen, though each change reaches only a few of its IDs. Ocarina of Time registers something between most of its draws, its streamed textures map and unmap virtual pool runs dozens of times a second, finalized descriptors stay registered, and textures take over each other's storage (alias_take_over), which bumped g_ser_gen with no note and so also sent every texture ID down the slow path. TheekshanaA measured about 53 s of a seven-minute walk through Kokiri Forest spent on 61,000 such builds (PR seedds#1405, seedds#1464). This is TheekshanaA's branch oot-pool-table-notes, ported to main's walk by PucaVaz, with the table patching of seedds#1299 / seedds#1387 it sat on: - A registration records its generation and ID (g_reg_log), a storage change its serial (g_ser_log, beside the notes ser_sync takes). A table built at an earlier generation takes again only the IDs they name, by the walk's own rules (pool_entry), and is kept when no entry changes, else copied and patched. - A memory pool added or removed records REG_POOL and no ID: a table takes again only the IDs it found without storage (pool_snap.dead); treg_gone notes each ID of a finalized pool. - A table keeps the IDs whose entries are views of retired registrations, those the read scan writes in place among them (pool_snap.trans); the next submission takes only those again, and such a table does not stand under POOL_SUB_MS. - A takeover records a note that names no texture (ser_note(0)): it changes no serial's texture (an entry a live serial holds is not evicted, serial_table_holds; one a finalized serial holds is only unref'd). A live sRGB / linear twin's view of an evicted texture is found stale by tex_mtl_ and dropped (serial_del), a note naming it. - A patch makes textures only within what the frame may still make (pool_room). Past POOL_IDS_MAX IDs, a note the ring has overwritten, a bump without a note, a resized table or a sparse mapping, the table is built whole as before. The log line says why; the power lines gain "[nvn] pool tables up to frame ...". On landing: the comment and the reference entry name serial_table_holds (seedds#1439 replaced the serial table walk) and describe the sRGB / linear twin case; a registration note is read as a sequence lock (reg_read: its generation is checked before and after its ID, and the writer clears it first), as memory pool changes write notes from threads that do not hold g_rmu and a note the ring overwrote during the read could be taken torn. Checked: t_pool_notes 81 checks (patched tables compared with tables built whole), t_pool_chunks 102, t_alias_takeover 28, t_gmem_words 16, t_feedback 159, t_image_alias 329, t_sparse_tiles 4,253, t_cpu_tex --metal 68, all 0 failed; test_docs; the Mac host builds and the iOS compile check passes (261 files). Ocarina of Time on an M-series Mac, hosts at 66e6e4c with and without this (main's chain has no libraries on disk for it), the village walk from save_ref: tables built whole 4,000+ (5.85 s+) on the base, 12 and 16 (0.11 s, 0.12 s) with this; submit p50 in the village 11-16 ms on the base, about 7-12 ms with this; the same red lines and no GPU error. Vertical stripes in both sides' shots at the same balcony points come from that old-chain setup, not this change. The author measured submit p50 12-15 -> 3-5 ms and frames over 50 ms walking 23 and 48 -> 3 and 2 (M4, two runs per side). No second game: none local has libraries for main's chain, and the one whose regress runs reach these tables (Paper Mario: The Thousand-Year Door) needs 11,861 shaders translated first. Co-authored-by: PucaVaz <65235669+PucaVaz@users.noreply.github.com> Co-authored-by: TheekshanaA <10327965+TheekshanaA@users.noreply.github.com>
…keover evicts the texture its view was made of: since PR seedds#1489 (0c7e88b) alias_take_over records the eviction as ser_note(0), which names none of the twin's IDs, so they kept the cached view on the fast path (tex_fast_rid) and the pool tables, patched or built whole, kept its resource ID, while the parent left the sharing table and, once the takeover's submission completed, the residency set (issue seedds#2125). The stale-view check in tex_mtl_ that the takeover's comment relied on ran only when something named the twin's texture object (a render target, a copy, a clear); before seedds#1489 the takeover's bare g_ser_gen++ sent every ID there. tex_mtl_'s twin path now lists the serial that took the view against its parent (g_twin_par, up to 8, under g_smu), only after checking under the same lock that the parent is still the sharing table's entry for its key; otherwise a takeover on another thread (tex_mtl_ also runs on the present thread and in mtl_read_texels) evicted it in between and found no list, so the view is dropped and the key looked up again. alias_take_over notes each listed serial of a texture it evicts, before alias_del removes its entry, so only those IDs take the slow path, where tex_mtl_ finds the view stale, and the tables are patched for them alone. A list that fills drops its finalized serials (a retired ID's view goes at the submission's end anyway), and one still full of live serials makes the takeover's bump a bare g_ser_gen++. alias_del, which 352e4a6 (PR seedds#2229) made the sharing table's one remover, drops the list of what it removes, so a pool's release (mtl_alias_drop_pool, whose bump already has no note) drops it too; the format-view upgrade, which replaces the texture in place and bumps with no note, drops it and lists the asking serial again for the view it makes on the upgraded texture. The reference entry (A storage takeover notes the serials of an sRGB / linear twin of what it evicts) says so. t_pool_notes's twin case (IDs 7 / 8 / 9) passed only because the note of ID 8's first resolution took ID 8 again in the takeover's walk: it now takes one walk between the two, as the issue proposes, which fails 2 of 83 checks on the old code, and its checks move one walk later, since the takeover's notes reach the walk after it; it keeps ID 8's first entry instead of reading that table's buffer again, which is used again once its submission completes. A new twin list case checks that a list that fills drops its finalized serials (only the live twin is noted) and that one full of live serials makes the bump bare. Checked on 352e4a6 and, after a rebase onto 8b55ebb (shader build tools and docs only), t_pool_notes, t_alias_takeover and t_feedback again there; M3 Max, macOS 26.7.2: t_pool_notes 119 checks, 0 failed, three runs (the twin case alone fails 2 of 83 on unmodified 352e4a6, three runs; taking the twins' notes out of alias_take_over fails 3, the bare-bump fallback 1); t_alias_takeover 45 checks and t_image_alias 337 with and without the encoder thread, 0 failed; make -k -C dm/gfx/tests GAME=0400486024DEC000 run: every test passes but, in some runs, t_feedback's check "with nothing running: a commit" (1 of 278), which fails as often on unmodified main and is unrelated (t_refl and t_rt_range_ro skip the reflection samples this Mac does not have); docs_symptoms.py --check; test_docs; the Mac host builds with main's 58 warnings. Not run: a game (no visible symptom is known, and Ocarina of Time, the only game on this Mac, was not built on main), regress.py (the reach is takeovers of a texture an sRGB / linear twin was made of), iOS
What and why
pool_gpuwalked a table of handles traced to no slot whole wheneverg_reg_genorg_ser_genchanged, though each change reaches only a few of its IDs. On main (6121fd7), TheekshanaA measured about 53 s of a seven-minute walk through Ocarina of Time's Kokiri Forest going to 61,000 such builds of its 33,025-entry texture table (#1405, #1464). The game registers something between most of its draws. Its streamed textures map and unmap virtual pool runs (vrun_set) dozens of times a second. Finalized descriptors stay registered, and textures take over each other's storage: about 8,400 takeovers in six minutes, each ag_ser_genbump with no note, which also sent every texture ID down the slow path (#1326).This ports TheekshanaA's branch
oot-pool-table-notes(95797683,9d486f88) to main, as asked on #1464. Those two commits extended the table patching of #1299 / #1387, which main did not take, so this PR brings that patching along, rebuilt on main's walk:g_reg_log), and a storage change records its serial (g_ser_log, next to the notesser_synctakes). A table takes again only the IDs they name, by the walk's own rules (pool_entry). It is kept when no entry changes, and copied and patched otherwise.REG_POOL, with no ID. Only the IDs found without storage are taken again (pool_snap.dead), andtreg_gonenotes each ID of a finalized pool.pool_snap.trans), and the next submission takes only those again.ser_note(0)) instead of a bump with no note.POOL_IDS_MAX, a note the ring has overwritten, a bump with no note, a pool that grew, or a sparse mapping. The log line says why tables were built whole and how many were patched or kept. The power lines gain[nvn] pool tables up to frame ….Where main's walk differs from the branch's base:
pool_room, split out ofpool_walk_limit). A texture left out counts in the table'sleft, as in a walk.pool_table_take/pool_table_give.pool_seen_scan) writes a retired registration's view in place, that view is listed too. A table holding such views from an earlier submission no longer stands underPOOL_SUB_MS.alias_sweep, and Add a Switch 2 profile for The Legend of Zelda: Ocarina of Time #1387's exception that kept a retired view of a shared texture past its submission, are not needed here. Main drops every retired view at the end of its submission, so the next submission takes those IDs again.TheekshanaA measured the branch on #1387's host (MacBook Pro M5 Pro, a six-minute walk through Kokiri Forest). Tables built whole went from about 9,600 (26 s) with only the pool notes to 14 (0.3 s) with all three. Reference entry: Pool tables patched instead of built whole. Ocarina of Time's note links to it.
Checked
GAME=0400486024DEC000on an Apple M4 (24 GB) running macOS 27.2:t_pool_notes(new): 81 checks, 0 failed. It covers a pool elsewhere, the pool that brings an ID's storage, a finalized pool, a bump with no note, a retired ID across submissions, a takeover, a registration past the budget, a read written in place, and a live sRGB / linear twin whose shared texture a takeover evicts. Each case patches or keeps the table, and the result is compared with the table built whole. With the takeover note, the retake at a new submission, or the in-place record taken out, it fails (2, 4 and 2 checks).dm/gfx/teststhat reach registrations, pool tables, serial notes or the sharing table all pass on the branch (rebased since onto main 0f510fd, wheret_pool_notes67/67,t_pool_chunks102/102,t_feedback159/159 andt_image_alias329/329 were run again and the Mac host builds). Among them:t_pool_chunks102 checks,t_feedback193,t_sparse_tiles4,253,t_image_alias329,t_nvn236,t_readback307 cases,t_linear_readback48 cases. The fullmake runwas not run.test_docs.py: 11 tests OK.docs_symptoms.pywas run.make -C dm/host GAME=0400486024DEC000), andnvn_mtl.mandhle_nvn.ccompile for the iPhone SDK (with the flags ofdm/ios/tools/compile_check.py).88c7e03f) and this branch, each in a clone of the same app (shader chain adec7d81d970): load, 180 s in the house, out of the door through Saria's greeting, then 300 s of stick pushes along the balcony.regress.py impact origin/main..HEADnames Breath of the Wild, Pokémon Scarlet and Tears of the Kingdom. Ocarina of Time has no usage manifest.regress.py check --gameshas not been run yet.Docs
docs/reference/nvn-objects.mdentry and in Ocarina of Time's note, andpython3 dm/tools/docs_symptoms.pywas runCo-authored with @TheekshanaA (their branch oot-pool-table-notes)