Repository navigation
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
Conversation
…deadline, so iOS no longer runs them on efficiency cores at 30 fps Only the thread that acquires the window declared a time-constraint policy. iOS's performance controller sizes the clock so that each declared thread fits its period, and that thread fits 33 ms on efficiency cores; the threads that make a frame late (queue replay, encode) had no deadline. They now take one sample of their own CPU time per presented frame (nvn_rt.h) and declare the frame's period with their own computation after a first window of 120 frames. The thread policy line names each declaring thread with its priority, its demoted frames and its CPU time and clock a frame (the old line read the presenting thread, which in Ocarina of Time is the game's own). DM_RT=0 turns every declaration off, DM_RT_FRAME=0 keeps only the acquire thread's. iPhone 17, Kakariko, 7 minutes each back to back: 26.4 -> 29.1 fps, frame p95 45.4 -> 38.1 ms, submit 15.2 ms at 1.67 GHz -> 13.2 ms at 1.78 GHz. Open: a slow GPU state of the phone (9-13 fps with or without the change), in docs/reference/frame-rate.md.
Only the thread that acquires the window declared a time-constraint policy. iOS's performance controller sizes the clock so that each declared thread fits its period, and that thread fits 33 ms on efficiency cores; the threads that make a frame late (queue replay, encode) had no deadline, and at Ocarina of Time's 30 fps cap on an iPhone they ran on efficiency cores. They now take one sample of their own CPU time per presented frame (nvn_rt.h) and declare the frame's period with their own computation after a first window of 120 frames. The thread policy line names each declaring thread with its priority, its demoted frames and its CPU time and clock a frame (the old line read the presenting thread, which in Ocarina of Time is the game's own). DM_RT=0 turns every declaration off, DM_RT_FRAME=0 keeps only the acquire thread's. iPhone 17, Kakariko, 7 minutes each back to back: 26.4 -> 29.1 fps, frame p95 45.4 -> 38.1 ms, submit 15.2 ms at 1.67 GHz -> 13.2 ms at 1.78 GHz, CPU 0.78 -> 0.87 W. Open: a slow GPU state of the phone (9-13 fps with or without the change), in docs/reference/frame-rate.md. Co-authored-by: Stan Bono <3304418+stan69b@users.noreply.github.com>
|
Landed on main as 4c71083, with a follow-up b06f4e0 that folds in PR #2137: each thread that acquires the window now has its own time-constraint slot (its own CPU clock, policy and port), and an acquire thread that stops acquiring for 120 frames goes back to the standard policy. OCTOPATH TRAVELER II acquires from two threads, so this was reachable. You are credited as co-author. Mac regression check: Mario Kart World and OCTOPATH TRAVELER II pass. Octopath had GPU stalls (one command buffer taking 120-160 ms of GPU time against 3-9 ms of encoder work) in 2 of 9 runs with the frame threads declaring. Both came within half an hour of each other, and none came in the next 7 runs nor in 18 runs without the declarations. Whether the declarations play a part is open in docs/reference/frame-rate.md; Still open: the combined phone run of batch OoT-fps (item A-2) for a second iPhone measurement. |
|
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. |
…dds#2569) Only the thread that acquires the window declared a time-constraint policy. iOS's performance controller sizes the clock so that each declared thread fits its period, and that thread fits 33 ms on efficiency cores; the threads that make a frame late (queue replay, encode) had no deadline, and at Ocarina of Time's 30 fps cap on an iPhone they ran on efficiency cores. They now take one sample of their own CPU time per presented frame (nvn_rt.h) and declare the frame's period with their own computation after a first window of 120 frames. The thread policy line names each declaring thread with its priority, its demoted frames and its CPU time and clock a frame (the old line read the presenting thread, which in Ocarina of Time is the game's own). DM_RT=0 turns every declaration off, DM_RT_FRAME=0 keeps only the acquire thread's. iPhone 17, Kakariko, 7 minutes each back to back: 26.4 -> 29.1 fps, frame p95 45.4 -> 38.1 ms, submit 15.2 ms at 1.67 GHz -> 13.2 ms at 1.78 GHz, CPU 0.78 -> 0.87 W. Open: a slow GPU state of the phone (9-13 fps with or without the change), in docs/reference/frame-rate.md. Co-authored-by: Stan Bono <3304418+stan69b@users.noreply.github.com>
…dds#2367 (5b0f137) replaces the batch's; the encode thread declares the frame deadline (seedds#2569) beside replay_ahead's hold check
Batch OoT-fps, session A, item A-1.
Problem
At the 30 fps cap on an iPhone, Ocarina of Time kept its frame's CPU work on efficiency cores:
Cause
Only the thread that acquires the window declared a time-constraint policy.
The summary's
thread policyline also checked the wrong thread. It read the policy of the thread that presents, which in this game is the game's own presentation thread. So it reported "not in effect" while the main thread held the policy (priority 97 in a spindump).Fix
The fix is in
nvn_rt.h(pure logic, tested byt_thread_rt) andhle_nvn.c, and applies to every game.thread policyline now lists each declaring thread with its priority, how many frames it was demoted below the real-time band, and its CPU time and mean clock a frame.DM_RT=0turns every declaration off. The newDM_RT_FRAME=0keeps only the acquire thread's declaration.development.mdandenv_switches.tsv.Phone, before and after
iPhone 17 (iOS 27.2.1), Kakariko Village, the unattended camera route (
iphone_drive_batch.sh), 7 minutes each, back to back. Builds:oot-build-vis(6725bc14) as the baseline, and the same build with this commit.The phone was at thermal state 2 for both runs.
Open: a slow GPU state. An earlier pair on the same route, with either build, went into another state of the phone:
The baseline went into it as well, so this change does not cause the state. Whether the CPU clock takes the GPU's share of the power budget there, and whether more declared deadlines make that worse, is not known. It is recorded as open in the reference entry.
Tests
dm/gfx/tests: t_thread_rt 8/8, t_fstep 86/0, t_nvn 314/0, t_gfx_suspend 22/0.dm/tools/tests/test_docs.pyandtest_env_switches.py: OK.Regression check
Pending. The change is host-only and does not touch the shader chain. Its effect is on iOS scheduling, which the Mac harness does not measure. The combined phone run of batch OoT-fps (item A-2) will add numbers here.