Skip to content

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

Closed
PucaVaz wants to merge 2 commits into
seedds:mainfrom
PucaVaz:pool-table-notes

Conversation

@PucaVaz

@PucaVaz PucaVaz commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

What and why

pool_gpu walked a table of handles traced to no slot whole whenever g_reg_gen or g_ser_gen changed, 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 a g_ser_gen bump 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:

  • A registration records its generation and ID (g_reg_log), and a storage change records its serial (g_ser_log, next to the notes ser_sync takes). 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.
  • A memory pool added or removed records REG_POOL, with no ID. Only the IDs found without storage are taken again (pool_snap.dead), and treg_gone notes each ID of a finalized pool.
  • Views of retired registrations are listed (pool_snap.trans), and the next submission takes only those again.
  • A storage takeover records a note that names no texture (ser_note(0)) instead of a bump with no note.
  • Anything else still builds the table whole, as before: a list past 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:

  • A patch makes textures only within what the frame may still make (pool_room, split out of pool_walk_limit). A texture left out counts in the table's left, as in a walk.
  • Patched tables go through pool_table_take / pool_table_give.
  • When the read scan (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 under POOL_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

  • Tests, all with GAME=0400486024DEC000 on 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).
    • 36 other tests in dm/gfx/tests that reach registrations, pool tables, serial notes or the sharing table all pass on the branch (rebased since onto main 0f510fd, where t_pool_notes 67/67, t_pool_chunks 102/102, t_feedback 159/159 and t_image_alias 329/329 were run again and the Mac host builds). Among them: t_pool_chunks 102 checks, t_feedback 193, t_sparse_tiles 4,253, t_image_alias 329, t_nvn 236, t_readback 307 cases, t_linear_readback 48 cases. The full make run was not run.
    • test_docs.py: 11 tests OK. docs_symptoms.py was run.
    • The Mac host builds (make -C dm/host GAME=0400486024DEC000), and nvn_mtl.m and hle_nvn.c compile for the iPhone SDK (with the flags of dm/ios/tools/compile_check.py).
    • clang-tidy: run with LLVM 22.1.1, because CI's 23.1.4 was not available here. It finds nothing in the changed files that main's same files do not have.
  • Games run: Ocarina of Time (v131072), four scripted runs on the same M4 (24 GB, macOS 27.2) from the autosave at Link's house, handheld at upscale 2.0, alternating main (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.
    • On main the table of handles traced to no slot was built whole 44,000 and 24,000 times per run, 67 s and 72 s of building in about 11 minutes; in the house alone, 23,000 and 7,000 builds took 26.5 s and 23.8 s of 182 s.
    • With this branch it was built whole 16 times while the save loaded (3 new tables, 11 or 13 storage changes, 2 registrations in one run; 52 ms and 19 ms) and about 100 times per run in all (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.1 and 14.8 ms to 3.1 and 5.3 ms in the house, and from 12.8 and 13.9 ms to 3.6 and 5.2 ms walking.
    • Frame time p50 did not change, since the GPU sets it (22 ms in the house, 23–24 ms walking). In the house, p95 went from 26.9 and 31.9 ms to 23.6 and 23.8 ms, and p99 from 34.6 and 41.0 ms to 24.2 and 25.6 ms. Walking, frames over 50 ms went from 23 and 48 to 3 and 2.
    • The picture (screenshots at the same points), footprint and the log's red lines were the same in all four runs, with no GPU errors. The route stays on the balcony; it is not TheekshanaA's walk through the village.
  • regress.py impact origin/main..HEAD names Breath of the Wild, Pokémon Scarlet and Tears of the Kingdom. Ocarina of Time has no usage manifest. regress.py check --games has not been run yet.

Docs

  • The finding is in its docs/reference/nvn-objects.md entry and in Ocarina of Time's note, and python3 dm/tools/docs_symptoms.py was run
  • No game files, keys or data extracted from a game in the change or the attachments

Co-authored with @TheekshanaA (their branch oot-pool-table-notes)

…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>
@lunks

lunks commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Thanks for porting this onto main. One question about alias_take_over: it now calls ser_note(0) instead of bumping g_ser_gen with no note, so a takeover no longer sends every cached texture ID down the slow path. The comment gives the reason: an evicted texture is retired only behind this submission's fence, and only a retired ID can still hold a view of it, since a live serial's texture is not evicted.

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 alias_take_over only evicts entries no serial holds, that holds; if an ID can still resolve to one through rr->t with a serial, it would not. A line in the comment naming what guarantees it would settle it.

…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.
@PucaVaz

PucaVaz commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, good question, and the comment did overstate it. What holds it is that alias_take_over changes no serial's texture: it skips an entry a live serial holds in g_by_serial (serial_holds), and an entry a finalized serial holds through g_ghost is only unref'd, so tex_by_id's slow path (treg_tex) would give a cached view the same object.

"Only a retired ID can still hold a view of it" was not exact, though: a live sRGB / linear twin (srgb_twins in tex_mtl_) holds a view of the shared texture, not the texture, so serial_holds doesn't see it and the base can be evicted while the twin is live. That case is covered by main's stale-view check in tex_mtl_: a view whose parent has left the sharing table is dropped (serial_del), which notes that serial, so the patch takes the twin's ID again instead of keeping a view of the old texture.

e4cc4bb rewords the comment to say this and adds the case to t_pool_notes (81 checks, 0 failed): ID 8 is the sRGB twin of a texture whose ID was finalized, then a texture of another description takes the storage over and evicts the shared one. In that same submission the log shows stale sRGB / linear view dropped ser=8, ID 8 gets its new texture, no table is built whole, g_treg_ep doesn't move, and the patched table matches a table built whole.

gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
# Conflicts:
#	dm/gfx/nvn/nvn_mtl.m
#	dm/gfx/tests/Makefile
seedds added a commit that referenced this pull request Nov 6, 2026
…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>
@seedds

seedds commented Nov 6, 2026

Copy link
Copy Markdown
Owner

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: t_pool_notes 81/81, t_pool_chunks, t_alias_takeover, t_gmem_words, t_feedback, t_image_alias, t_sparse_tiles and t_cpu_tex --metal all pass; the Mac host builds, and the iOS compile check passes. Ocarina of Time, the village walk from a save near Link's house, hosts at 51cc8bc with and without this change (main's current chain has no libraries on disk here): over 4,000 tables built whole (5.85 s or more) on the base, 12 and 16 (about 0.1 s) with this change; submit p50 in the village went from 11-16 ms to about 7-12 ms on this Mac, with the same red lines and no GPU errors.

@seedds seedds closed this Nov 6, 2026
@jonpage0

jonpage0 commented Nov 6, 2026

Copy link
Copy Markdown

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 serial_holds (a walk of the whole serial table per entry), alias_take_over asks a per-object hold count kept under g_smu: tex_put and serial_del add and remove a count for the object and for every parentTexture up the chain, and an NvPkg counts its tex, view and spare members (a member swap moves the count). So a live sRGB / linear twin, which holds a view of the shared texture, keeps the base: the base's count is non-zero through the view's parent, and the takeover skips it. The takeover then records ser_note(0) as this PR does.

Checked on main bd9f422 with #1489 applied first: t_pool_notes extended with that case (a live twin keeps the base and its cached view while the patched table agrees with a whole rebuild), 81 checks in both encoder modes; a takeover test with parent and package-member hold removal, 36 checks per mode; t_image_alias 329, t_nvn 236, t_pool_chunks 102, 0 failed. The adaptation is in our tree (local/oot-c-prs); happy to open it as a PR on top of this one if useful. Gameplay numbers for the combined host are in the thread on #1439 once the route runs finish.

Aznracer pushed a commit to Aznracer/Mighty-Green-Kokiri-Kid that referenced this pull request Nov 6, 2026
…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)
cheemu21 pushed a commit to cheemu21/kid-with-sword-remake that referenced this pull request Nov 7, 2026
…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)
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.
m-gnr pushed a commit to m-gnr/kid-with-sword-remake that referenced this pull request Nov 7, 2026
- 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
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…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>
dean0396 added a commit to dean0396/nxnative that referenced this pull request Nov 11, 2026
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation game: 0400486024DEC000 A game 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.

4 participants