Repository navigation
Conversation
a478810 to
600d3c7
Compare
…ture views: every submission that read such an ID made its Metal view again, about 310 of the 330–347 views Ocarina of Time (Switch 2) made a frame in Kokiri Forest The view cache kept views only for live textures. A texture finalized while one of its IDs is still registered resolves through g_ghost, and vc_set_for refused its set because nothing would drop it. tex_by_id then made a transient view that treg_finish_submission released at the submission's end, and the next submission made it again. Such a texture's set is now kept, marked as a finalized texture's. Once a frame, from tex_by_id, vc_ghost_sweep drops the sets of the serials g_ghost has let go. The ring evicts under g_smu, which can't take g_rmu. The views hold the texture only as long as g_ghost already does, plus up to a frame. DM_NVN_VC_KEEP (default 1): 0 turns it off, ab alternates it with the DM_AB phases. In-run A/B on Ocarina of Time's Kokiri Forest route (M2 Air 16 GB, 20 pairs of 240-frame phases): - views made 330–347 → 38 a frame, none from eviction; - the submit thread −1.72 M cycles a frame [−1.91, −1.49] and −5.37 M instructions [−5.65, −5.06]; - frame time within noise (GPU-bound). On main 0c23e0b (serial tables in nvn_owner.m, seedds#2389's ghost table kept by use): vc_set_for and vc_ghost_sweep ask the owner through own_ghost_has / own_ghost_has_locked, new: a look at the ghost table that is not a use, so a cached view never keeps its serial in the table. Every tex_by_id lookup still resolves the ID's texture (a use), so a finalized texture the game reads stays in the table, and its set with it; once the owner lets an idle entry go, the next sweep drops the set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
600d3c7 to
4e5750d
Compare
…eps its views until the ghost table lets it go (PR #2482) The view cache (vc_get / vc_put) kept views only for live textures. A texture the game finalized while one of its IDs is still registered resolves through g_ghost, and vc_set_for refused its set, as nothing would drop it. So every submission that read the ID made a transient Metal view, released at the submission's end, and the next submission made it again: about 310 of the 330-347 views Ocarina of Time (Switch 2) made a frame in Kokiri Forest. Such a texture's set is now kept, marked as a finalized texture's. At the first commit of each frame, right after own_ghost_trim, vc_ghost_sweep drops the sets of serials the ghost table no longer holds (the eviction runs under g_smu, which cannot take g_rmu). The checks look at the ghost table without counting as a use (own_ghost_has), so a kept view does not keep its texture in the table. A texture buffer's 2D rows stay out of these sets: a finalized texture's rows are kept by the memory they view (g_tb2_gone), which other textures' descriptors over that memory share. DM_NVN_VC_KEEP (default 1): 0 turns it off, ab alternates it with the DM_AB phases. The view-cache log line counts the finalized textures' sets. Measured in the PR, on a line before #2389 (in-run A/B, OoT Kokiri Forest, M2 Air, 20 pairs): views made 330-347 -> 38 a frame, submit thread -1.72 M cycles a frame [-1.91, -1.49], frame time within noise (GPU-bound). Not measured again on main: no OoT libraries on disk. Checked: t_vc_ghost (new; 19 checks, 17 with DM_NVN_VC_KEEP=0), t_nvn, t_serial_owner, t_rt_lifetime, t_queue_lifetime, t_cpu_tex, t_pool_notes, t_tbuf2d, t_tbuf_align, t_readback, t_alias_takeover, t_upgrade_alias, t_pool_chunks; regress.py check on Bastion (makes no views) and Return of the Obra Dinn (4 finalized sets kept; its intermittent GPU hang hit baseline and candidate alike over 3 runs a side): fps and frame times unchanged. Co-authored-by: dpadr <34929160+dpadr@users.noreply.github.com>
|
Landed on main as 9339e55, with you as co-author. Thank you: the diagnosis was right, and so was fixing it in the view cache. What changed from this branch:
Checks: the gfx tests above, plus Not measured again: the OoT gain on current main. There are no OoT shader libraries on this Mac right now. Your |
Texture views were kept by the serial that asked (vc_get), with the views of finalized textures read through a registered ID in sets of their own (PR #2482, DM_NVN_VC_KEEP). Ocarina of Time initializes about 40 render targets again every frame over the same storage, each a new serial in a new texture ID; the sharing table hands each one the Metal texture the last frame's had, but its views (depth read as (R, 0, 0, 1), RGBX with alpha 1, sRGB views, image slots' writable views, which img_fit kept only by ID) were made again every frame on the submit thread. A view depends only on the texture it views and its parameters, so the cache is now keyed by the Metal texture, for sampled and image views alike: one lookup, no parentTexture check per hit. A view retains its parent, so the key cannot name another texture while its entry exists. An entry is made only while the owner holds its texture (a live or ghost entry, a pin or the sharing table: own_holds_locked, by address) and goes at the first commit after the owner lets it go: the owner lists the objects that leave their last holder (gone_note, at a count reaching zero, a texture leaving the sharing table, a rename) and vc_commit takes the list (own_gone_take_locked); past 65,536 between two commits it checks every entry instead. A pool's finalize still drops its views. This replaces the cache by serial, PR #2482's finalized textures' sets with DM_NVN_VC_KEEP (removed), and the cache by shared texture the PR first added beside them. Texture buffers' 2D rows are kept with the buffer texture while it is live, as before by serial. Measured on an M4 Pro (20-core GPU, macOS 27.2), Ocarina of Time, kokiri-deck with scripts/bench.py, three runs a variant against main f43284e (chain a3ee07f000cb): views made about 40 a frame -> about 1.5 (118,209-120,204 -> 4,397-4,711 by frame 5,700, image views now counted too), 2,349-2,406 -> 700-786 textures holding views; frame time unchanged (the game's 30 fps step), no red lines, the pictures right. Submit-thread time not measured on its own (about 0.2 M cycles a frame at PR #2482's 5,900 cycles a view). Checked: t_view_reuse (19, the PR's test adapted), t_vc_lifetime (19, replaces t_vc_ghost: ghost reuse with no view made, the ghost table alone holding, pool finalize, the list's overflow scan) and t_view_swizzle, t_nvn, t_alias_takeover, t_upgrade_alias, t_image_srgb, t_image_usage, t_serial_owner, t_read_owner, t_view_levels, t_stencil_view, t_srgb_alias, t_pfv, t_cpu_tex, t_feedback, t_partial_upload, t_rt_lifetime, t_queue_lifetime, t_pool_notes, t_tbuf2d, t_tbuf_align, t_pool_chunks; lint_c tidy; test_docs. regress.py check on OCTOPATH TRAVELER II, two runs a side: PASS (frame p99 18.16/18.03 -> 17.92/18.0 ms, same shaders, no new red lines). Not run: Age of Calamity (the texture-ID ring the serial cache was made for; no build on this Mac), the iPhone. Co-authored-by: stan69b <3304418+stan69b@users.noreply.github.com>
|
A correction to my reply above: keeping a finalized texture's texture-buffer rows out of its set was wrong, and your branch had it right. Measured on Yoshi and the Mysterious Book (M6, from an empty save to the opening dialogue): its menus read finalized textures' rows only, and Main has since moved to the cache keyed by Metal texture (#2542), which kept the live-only rule. 8d003e1 removes it, so a finalized texture's rows stay with it while the owner holds it. To frame 5,100, 114,091 binds made 40,308 rows views before; with the change, 169,774 made 788. You're credited as co-author there too. |
…d-texture view sets take views the cache by Metal texture (seedds#2260) finds; seedds#2366's upscaler scales behind mfx_order; t_sbw keeps seedds#1320's and main's cases
What and why
Symptom. Creating texture views (
newTextureViewWithPixelFormat) was the largest single cost on Ocarina of Time's (Switch 2) submit thread. The view cache hit, yet the game made about 330 views a frame in Kokiri Forest, and about 310 of them were the same views made again.Cause. The view cache (
vc_get/vc_put, Texture views cached by texture and view parameters) kept views only for live textures.g_ghost.vc_set_forrefused that texture's set, because nothing would have dropped it later.treg_finish_submissionreleased at the end of the submission, and the next submission made it again.Change.
g_ghoststill holds keeps its view set, marked as a finalized texture's.vc_ghost_sweep, called fromtex_by_id's slow path underg_rmu, drops the sets of serials thatg_ghosthas let go.g_smu(own_ghost_trim), which can't takeg_rmu, so the eviction itself can't drop the set.own_ghost_has/own_ghost_has_locked: a look that is not a use, so a cached view never keeps its serial in the table (Finalized textures in use stay in their table: no 100–490 ms frames at a streamed transition on 0400486024DEC000 #2389 keeps entries by use). Everytex_by_idlookup still resolves the ID's texture, which is a use, so a finalized texture the game reads stays, and its set with it.g_ghostalready does, plus up to a frame.DM_NVN_VC_KEEP: on by default;0turns it off, andabalternates it with theDM_ABphases for an in-run A/B.Why on by default. This is general host code. It changes no translated output, only whether an identical Metal view is made again. Each kept set lives no longer than its ghost entry (plus up to a frame), and all sets together are bounded by
VC_MAX, and each set is dropped by the same paths as before (the texture's pool finalized,VC_MAX) plus the sweep. With 4 slots per texture as before, OoT showed no evictions (0.0 a frame), so the slot count is unchanged.Relationship to #2260 (open, roandegraaf: views cached by the Metal texture, behind this cache). The two fix different misses of the same cache, and they are complementary:
g_ghost. Its serial is stable, butvc_set_forrefused to keep its set, so a transient view was made every submission. Measured here: about 310 of the 330 views a frame in Kokiri Forest.view_pkg), which this change does, because nvn/views: cache texture views by Metal texture behind the serial cache #2260 leaves those out on purpose. This change also fixes the cause where it lies, the first cache's refusal, rather than adding a second lookup and lock per view.vc_get, thenvb_getwhen!view_pkg;vb_putaftervc_put). In code they merge without conflict: nvn/views: cache texture views by Metal texture behind the serial cache #2260 onto this branch conflicts only indm/gfx/tests/Makefile, as it does onto main itself.DM_NVN_VC_KEEP=ab) on a host with nvn/views: cache texture views by Metal texture behind the serial cache #2260. It will be reported here when our cut8 line runs it.Reference entry: Texture views re-made for finalized textures every submission; symptom index regenerated; a line under OoT's "Found on this game".
Checked
0400486024DEC000, v131072, Switch 2).Setup:
DM_AB=240, so phases of 240 frames alternating off and on; 21 + 21 phases from frame 1,800;Host: the same change on our cut4 line (these numbers were measured before the rebases onto main b21eb3f and then 0c23e0b, where the serial tables moved to
nvn_owner.mand Finalized textures in use stay in their table: no 100–490 ms frames at a streamed transition on 0400486024DEC000 #2389 changed how long the ghost table keeps entries; the view-cache logic is the same. A fresh in-run A/B on current main follows once our main reference build reaches that chain). Its other switch, 16 slots a texture, never came into play: 0.0 evictions a frame with the switch off.Results:
The views made with the change on (about 37 a frame) are new textures' first views, which no cache can avoid.
Frame time: the A/B ran mostly GPU-bound, with the CPU clock varying from 1.6 to 2.6 GHz over the run, so frame p50 moved within noise. The result is stated in cycles and instructions, which don't depend on the clock.
Why the cycles still matter. Burn controls on the same Mac and route added a fixed amount of submit-thread work (3 M cycles spun at each frame's first submission, in alternating
DM_ABphases):The M2 Air spends long stretches of play at that lower clock, so −1.72 M cycles is about −1 ms a frame there.
GPU p50: the +0.28 ms is small and has no identified cause. The change encodes the same views and the same draws, and per-pass GPU times (
DM_NVN_GPUPROF) are within noise.regress.py impact origin/main..HEAD(3fd81cd0 on main 0c23e0b):regress.py check --games: not run. The local NVN games with Mac builds (Animal Well010020D01AD24000, Mina0100DCD01F306000) have shader packages split before packages carried their shader keys, and main's f7a5452 (Ocarina of Time (0400486024DEC000) black screen with sound on c78d7375 / chain 89f44f9b6bb0: the present pass pairs VS a5b1bd74e47458d7 (writes loc0 only) with FS 29e3c9dd92b8a571 (reads loc1/loc2) #1841) refuses them, so the check stopped before any comparison, on the baseline and this branch alike. No regression check was done for this change.docs/env_switches.tsvand keeps nvn-mtl: write image buffer textures below 16-byte alignment through a shadow #1399's newtbvfields in the view-set struct beside theghostflag. On 4e5750d:make dmhost,t_nvn311 checks andt_serial_owner176, 0 failed. The full run:t_nvn311 checks, 0 failed (also withDM_NVN_VC_KEEP=0);t_serial_owner176/0;t_rt_lifetime9/0;t_queue_lifetime38/0;t_cpu_tex105/0;t_pool_notes91/0;t_readback218 cases, 0 failed.dm/tools/tests/test_docs.py(6 tests) andtest_env_switches.pyOK on 600d3c7 (run withpython3 -m unittest).VC_MAX, as above.Docs
docs/reference/rendering.mdentry and OoT's note, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index)🤖 Generated with Claude Code