Skip to content

OoT-fps C-5: the queue worker's per-node kernel-time reading keeps its thread port - #2545

Open
stan69b wants to merge 1 commit into
seedds:mainfrom
stan69b:pr/oot-fps-c5-thread-port
Open

stan69b wants to merge 1 commit into
seedds:mainfrom
stan69b:pr/oot-fps-c5-thread-port

Conversation

@stan69b

@stan69b stan69b commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

OoT-fps batch, session C (submit-thread CPU per draw), item C-5. The ranked list of where the submit thread's time goes (C-1) is in #2542.

The queue worker reads its thread's kernel time (thr_sys_ns) at the start and end of every node it replays (nq_cost_begin / nq_cost_end), for the translation detail line. Each reading called mach_thread_self and mach_port_deallocate around thread_info: two more kernel calls per reading, four per node. The port is now taken once per thread and kept (one send right per thread that reads it). For every game. In the ranked list the cost readings were 3–5 % of the submit thread's busy time; this removes two of their three kernel calls. With C-4 the standing scene moved from 38.7–39.3k to 38.6–38.8k instructions per draw (M4 Pro), within noise.

Checks

  • t_nvn (311 checks) passes unchanged; test_docs OK.
  • scripts/regress.py check --baseline upstream/main --games 0400486024DEC000 on the stack of C-2 to C-5 (Apple M4, macOS 27.2; Ocarina of Time's title route from the unmerged ed769e9 smoke.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_CAPTURE cannot 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.

…s thread port

nq_cost_begin / nq_cost_end read this thread's kernel time (thr_sys_ns) for every node the queue worker
replays, and each reading called mach_thread_self and mach_port_deallocate around thread_info: two kernel
calls more per reading, four per node. The port is now taken once per thread and kept.

t_nvn (311 checks) passes unchanged.
@github-actions github-actions Bot added the nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) label Nov 13, 2026
@dpadr

dpadr commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Ran on an M2 MacBook Air with Ocarina of Time as part of a set with #2542 / #2543 (in-run A/B to frame 12000 at handheld 1440x810): no regressions, no red lines, unit tests pass. This change was on in both arms, so we didn't measure it on its own; by our count of its call sites on this route it's in the ~0.01-0.03 ms a frame range.

@stan69b

stan69b commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

iPhone 17, Kakariko Village: C-2 to C-5 together, before / after

Unattended runs with the batch's drive script (iphone_drive_batch.sh, 7 minutes each, the owner's save in Kakariko, the launcher's 30 fps cap, dynamic resolution, MetalFX, encode thread on). Both apps are oot-build-vis (b32f7fc1 + the visibility ring) with the ring at 512 (ALLOC_MAX * ALLOC_SHARE, as upstream b515089); the after app adds C-2, C-3, C-4 and C-5. Run order base, C, C, base. Medians of the 300-frame summary lines once in the village (first two windows skipped).

run instructions per draw cycles per draw draws a frame submit M instructions a frame submit CPU a frame submit thread clock fps frame p50 submit+present p50 GPU p50
base 1 26,790 10,341 2,691 71.2 12.69 ms 2.21 GHz 29.2 33.7 ms 13.2 ms 30.8 ms
C 1 (slow GPU state) 24,886 11,279 2,706 67.3 8.96 ms 3.34 GHz 12.6 80.0 ms 9.6 ms 69.7 ms
C 2 24,853 9,219 2,771 68.9 14.01 ms 1.84 GHz 28.0 34.9 ms 14.8 ms 31.7 ms
base 2 27,784 10,538 2,783 77.4 16.48 ms 1.78 GHz 25.3 38.8 ms 18.0 ms 34.9 ms
  • Instructions per draw: 27,287 → 24,870 (−8.9 %), the same in both C runs (24,886 and 24,853); on the Mac it was −11 %. The batch's target (a third fewer) is not reached.
  • Submit CPU a frame follows the submit thread's clock more than the instructions: C 2 against base 2 at about the same clock (1.84 / 1.78 GHz): 16.48 → 14.01 ms (−15 %); cycles per draw 10,440 → 9,219 (−12 %). The 30 % target is not reached.
  • C 1 fell into the slow GPU state session A also met with both of its builds (OoT-fps A-1: the queue replay and encode threads declare the frame's deadline, so iOS no longer runs them on efficiency cores at 30 fps #2569: GPU 66-80 ms for the same draws, CPU at 3.3 GHz, 9-13 fps); only its instructions per draw compare.
  • Screenshots of all four runs show the village rendered the same way; the C runs log no red line the base runs do not (the only one in both is the pipeline archive pieces built for a later iOS). env.txt restored after every run; save untouched.

What is left of the submit thread's cost per draw (C-1's ranking in #2542): argument blocks (encode_args own time, pool_gpu, img_binding), pass opens (rp_begin_ex), render-condition operands (rc_value), state records and pipeline lookup, a few percent each, plus the residency commits (res_commit, about 8 %) and new render targets' textures (#2566's area).

@stan69b

stan69b commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

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).

baseline (512 ring) combined change
fps 28.6 29.4 +0.8
frame time p50 / p95 34.3 / 40.6 ms 33.6 / 37.1 ms p95 −3.5 ms
NVN submit+present p50 14.13 ms 12.25 ms −13 %
GPU p50 31.2 ms 30.4 ms −3 %
submit thread CPU a frame 13.47 ms at 2.05 GHz 12.00 ms at 1.82 GHz −11 % at a lower clock
instructions per draw 26,790 24,614 −8.1 %
draws per frame 2,680 2,770
share of CPU time on performance cores 49 % 51 %
CPU energy 0.99 W 0.84 W −15 %
residency set 3,499-3,561 MB 3,014-3,174 MB −0.4 GB
system wired 5,230-5,397 MB 4,808-4,950 MB −0.4 GB
memory-pressure warns 1 0
thermal state (max) 1 2
  • Both runs show the same screenshots and the same RED line: the stale pipeline archive message, which both builds print.
  • After each run, env.txt was put back byte for byte, and the save was not touched.
  • The game's real-time threads in the combined run were never demoted: 0 of 300 frames on the acquire, queue replay and encode threads.

Against A-1's targets:

  • Submit ≤ 15 ms: reached.
  • A steady 30 fps: nearly; 29.4 median, p95 37 ms.
  • Performance-core share ≥ 60 %: not reached (51 %).

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 end.png was checked for time of day, and only day runs are compared above.

The combined build is the one installed on the phone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

3 participants