Repository navigation
Conversation
…re, not only by the serial that asked Ocarina of Time initializes about 40 render targets again every frame over the same storage, each with 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 as (R, 0, 0, 1), RGBX with alpha 1, sRGB views, image slots' writable views) were kept by serial (vc_get, dropped at mtl_tex_release) or by texture ID (img_fit), so every frame made them all again on the submit thread: 84,748 views made against 169,005 reused by frame 4,200 standing on the Kokiri Forest deck (M4 Pro), about 2,000 with this change. Views of a texture in the sharing table are now kept by that Metal texture too (vcb_get / vcb_put), for sampled and image views. A view retains its parent, so a key cannot name another texture while its entry exists; entries whose texture left the sharing table go at a sweep once a frame (vcb_sweep). Textures outside the sharing table keep views by serial only. The memory report's view line counts the new hits. New test t_view_reuse (fails without the change). docs/reference/frame-rate.md entry, symptom index, game note line.
|
Tested on an M2 MacBook Air (16 GB, macOS 27.0, AC), Ocarina of Time, together with #2543 / #2542. We applied C-2 and C-3 on our test line (7f50222 base plus our own PR set) behind one runtime switch and ran an in-run A/B: alternating 240-frame phases on and off in the same run, along our scripted Kokiri Forest route to frame 12000 at handheld 1440x810, 60 fps cap. Two valid runs; a third ended early in an off phase (a stall we haven't attributed) and gave no data.
We couldn't separate C-2 from C-3 in this test; the desk estimate is about 0.25-0.33 ms for C-2 and 0.2-0.4 ms for C-3 of submit-thread time on this route. |
iPhone 17, Kakariko Village: C-2 to C-5 together, before / afterUnattended runs with the batch's drive script (
What is left of the submit thread's cost per draw (C-1's ranking in #2542): argument blocks ( |
|
OoT-fps combined phone build, against the baseline (session A, A-2) iPhone 17, Ocarina of Time, Kakariko Village by day, unattended drive script, 7 minutes each, back to back (2026-11-13, 22:20 and 22:36). Medians over the gameplay windows (more than 1,000 draws a frame).
Against A-1's targets:
An earlier pair that cannot be compared: the first pair (21:04-21:19) gave 11.4 fps for the baseline and 29.2 fps for the combined build. That gap is the time of day, not the change: the baseline's save loaded Kakariko at night (GPU p50 60 ms) and the combined build's by day, as B found (#2582). Every run's The combined build is the one installed on the phone. |
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>
|
Landed on main as 87e258b, credited to you with It landed in a different form. Since this PR was opened, #2482 added views kept by serial for finalized textures. With the cache by shared texture beside it, there were three view caches with three lifetimes, and they overlapped: for a shared texture, your cache served the view #2482's sets were made for. So the cache is now one, keyed by the Metal texture a view is of, for sampled and image views alike.
Measured on an M4 Pro (20-core GPU), Ocarina of Time,
Submit-thread time wasn't measured on its own.
|
… while the owner holds it tbuf_as_2d kept a buffer texture's 2D rows with the texture only while it was live (own_tex_live), so rows a finalized texture first asked for went to the table by what they view (g_tb2_gone), a direct-mapped 1,024-entry table. Yoshi and the Mysterious Book's menus read finalized textures' rows only, and that table missed on about a third of the binds. The view cache by Metal texture (PR #2542) already keeps an entry only while the owner holds the texture (a live or ghost entry, the sharing table) and drops it at the first commit after, so the rows now follow that rule as every other view does; g_tb2_gone keeps the rows of textures the owner no longer holds. Yoshi and the Mysterious Book, M6, from an empty save past the menus to the opening dialogue, 120 s, partial library on chain d6b05d7481e5: - to frame 5,100: 114,091 row binds made 40,308 rows views -> 169,774 made 788; - whole run: 804,505 binds made 108,882 -> 1,095,030 made 9,619; - frame time 16.7 ms either way (the game's 60 fps); submit time within noise in these windows. Checked: t_vc_lifetime (27 checks; the new rows check fails without this change), t_view_reuse, t_tbuf2d, t_tbuf_align, t_buffer_image_bias, t_upgrade_alias, t_nvn, t_serial_owner, t_alias_takeover; test_docs; lint_c tidy at its baseline. Co-authored-by: stan69b <3304418+stan69b@users.noreply.github.com> Co-authored-by: dpadr <34929160+dpadr@users.noreply.github.com>
|
Follow-up on main, 8d003e1: With the rows following the owner's hold, as every other entry of this cache does: to frame 5,100, 114,091 binds made 40,308 rows views before, and with the change 169,774 made 788. Over 120 s, 108,882 → 9,619. Frame time is unchanged (60 fps cap). |
PR #2542's entry gave an estimate. From the same three runs a side (M4 Pro, kokiri-deck), standing on the deck with per-frame instructions fitted against draws a frame at 2,180 draws: 73.93 -> 73.06 M instructions a frame (-1.2 %), submit CPU p50 9.40 -> 9.18 ms.
…he serial cache): superseded by seedds#2542, on main as 87e258b (one view cache keyed by the Metal texture); 0400486024DEC000 keeps enc_thread, which seedds#2341's replay_ahead needs
…y the Metal texture (seedds#2542) in place of seedds#2260's; the game's upscaler takes the game's textures straight (seedds#2559) except square-root input and a blended output (seedds#2366)
OoT-fps batch, session C (submit-thread CPU per draw), item C-2. This PR also carries C-1: where the submit thread's CPU per draw goes (below).
What changes
Ocarina of Time initializes about 40 render targets again every frame over the same storage, each with a new texture 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) were kept by serial (
vc_get, dropped atmtl_tex_release) or by texture ID (img_fit), so every frame made them all again on the submit thread.Views of a texture in the sharing table are now kept by that Metal texture as well (
vcb_get/vcb_putinnvn_mtl.m), for sampled and image views. A view retains its parent, so a key cannot name another texture while its entry exists, and a hit is a view of this very texture. An entry whose texture left the sharing table (pool finalized, storage taken over, upgrade) goes at a sweep once a frame (vcb_sweep). Textures outside the sharing table keep their views by serial only. The memory report's view line counts the new hits. The rule holds for every game.Measured (M4 Pro,
kokiri-deckroute with the Load confirmation, encode thread on as on iOS)Instructions per draw standing on the deck: within run-to-run noise (about ±1.5 %) on its own; the churn is larger while the camera turns. Together with C-3 (#2543): 43.3–43.7k → 38.7–39.3k.
C-1: where the submit thread's CPU per draw goes
sampleof theNVN queue replaythread, main 8d44aaf, Ocarina of Time on the Kokiri Forest deck (M4 Pro,DM_NVN_ENC_THREAD=1as on iOS, 650–1,050 draws a frame, 38–44k instructions per draw). Share of the thread's busy samples (waits excluded), inclusive:OP_DRAW/OP_DRAW_IDX→draw_)encode_args(draws and dispatches)pool_gpu8 % (texture pool table walk →tex_by_id),img_binding4 %,tex_by_id3 %,cbuf_slot1.5 %, own time 6 %-[AGXTexture newTextureView…]tex_by_id,img_binding)mtl_sub_commit(not waiting)up_flush→res_commit8 % (residency set commits: D's area); own time andgw_put6.5 %: the commit walked all 16,384 slots of the storage-write page set, ~30 times a framedispatch)encode_argsfb_preparerp_begin_exattach→tex_mtlrt_dims→tex_mtl→own_alias_publishrc_value/rc_sourcenq_cost_begin/nq_cost_endthread_info,mach_thread_self,mach_port_deallocatestate_op,pso_get,bind_vbufs,mtl_execown timerec_drain(waiting for the encode thread) is 12–40 % of the thread's wall time and is not counted above.Checks
t_view_reuse(16 checks; the reuse checks fail without the change).t_view_swizzle(554),t_view_levels,t_stencil_view,t_srgb_alias,t_image_srgb,t_image_usage,t_pfv,t_cpu_tex,t_feedback,t_alias_takeover,t_serial_owner,t_partial_upload,t_nvnpass;test_docsOK.scripts/regress.py check --baseline upstream/main --games 0400486024DEC000on the stack of C-2 to C-5 (Apple M4, macOS 27.2; Ocarina of Time's title route from the unmerged ed769e9smoke.tsv, as main has no route for it): shaders PASS (42848/42848, no MSL change), boot ok, fps p50 59.5 → 59.5, frame p99 17.82 → 17.81 ms, peak 2879 → 2872 MB, no new RED line. The check's status is REGRESSION because of its two screenshots (350 s, 385 s). They show different title backdrops (a forest at night vs Gerudo Desert; Lon Lon Ranch vs Lake Hylia), i.e. the backdrop rotation's timing, not a rendering difference. The same two screenshots were flagged between unrelated revisions on 2026-11-12 (0c07813 vs b8cb0bd), and with one run per revision the harness has no noise to compare against. A bit-exact replay check is not available for this game:DM_NVN_CAPTUREcannot capture its multiple NVN queues. Gameplay on the Kokiri Forest deck renders correctly in the bench runs. Other local NVN games (Echoes of Wisdom, Super Mario 3D All-Stars) were not run: pending.