Skip to content

Frame step: a probe's grace lasts while its frames keep getting cheaper - #1042

Closed
moretti wants to merge 1 commit into
seedds:mainfrom
moretti:fstep-probe-ramp
Closed

moretti wants to merge 1 commit into
seedds:mainfrom
moretti:fstep-probe-ramp

Conversation

@moretti

@moretti moretti commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Split out of #930 (2 of 4).

Summary

The Mac raises the GPU clock for a shorter deadline slowly, about 100 MHz a second. A frame-step probe back to hold 1 had 30 grace frames, which is too short for that, so the probe failed and the game stayed at 30 or 20 fps for minutes until a menu.

 probe back to hold 1: FSTEP_GRACE (30) frames
+  if the window's second half cost <= 95% of its first (FSTEP_RAMP)
+     and the window had a late frame
+       start another grace window, up to FSTEP_PROBE_MAX (180) frames in all
   a flat cost, or one that stops short of the hold, fails the probe as before

The change is in the pure controller (nvn_fstep.h), so it holds for every game. DM_FSTEP_PROBE_RAMP=0 turns it off; ab compares it in one run with DM_AB.

Docs: docs/reference/frame-rate.md ("A probe's grace lasts while its frames keep getting cheaper"), its symptom line, and the Breath of the Wild note.

Evidence

  • Before: Breath of the Wild 1.9.0, M5 Pro, fullscreen. A probe saw the clock rise 338 → 432 MHz and the GPU time fall 24.8 → 18.5 ms over its 33 frames, failed anyway, and the game stayed at 30 or 20 fps until a menu.
    After: in a 470 s run with the rule switched on and off every 30 s (DM_FSTEP_PROBE_RAMP=ab), every probe after the first 80 s held: 11 in a row, each within about a second. The probes that failed (presents 3834, 3929, 4083) were all in phases with the rule off.
  • t_fstep 70 checks (64 on main): a cost falling 0.6% a frame fails the probe without the rule and holds with it; a flat cost, a cost that stalls at 1.3 and a cost that falls without reaching the hold end at 33, 63 and 183 frames.
  • Builds and tests on its own on current main: host and CPU gfx tests, sanitizers, tool tests, clang-tidy and ruff/pyright pass locally.

Merge Danger

Door: two-way. DM_FSTEP_PROBE_RAMP=0 turns it off.

Blast Radius: frame pacing.

This changes the frame step for every game that has one, on every platform, iOS included: a probe can now last up to 180 frames while its frames keep getting cheaper. A flat cost still ends a probe after 30 frames, as before.

regress.py impact: Breath of the Wild, Pokémon Scarlet and Tears of the Kingdom. None of them is on this machine, so regress.py check --games was not run. Measured only on Breath of the Wild on the Mac.

On the Mac the GPU's performance controller raises the clock for a shorter deadline by about 100 MHz a
second. A probe back to hold 1 had 30 grace frames: Breath of the Wild on an M5 Pro saw the clock rise
338 → 432 MHz and the GPU time fall 24.8 → 18.5 ms over a probe, which then failed, and the game stayed
at 30 or 20 fps for minutes until a menu. A grace window whose second half costs at most 95% of its
first (FSTEP_RAMP) and had a late frame now starts another, up to 180 frames in all (FSTEP_PROBE_MAX);
a flat cost ends the probe as before. General, in the pure controller (nvn_fstep.h);
DM_FSTEP_PROBE_RAMP=0 turns it off, ab compares it in one run.

One 470 s run with the rule switched every 30 s: after the first 80 s every probe held, 11 in a row,
each within about a second; the failures were in a phase with the rule off.

t_fstep: a falling cost holds with the rule and fails without it, a flat cost lasts 33 frames as
before, a cost that stalls ends one window later, one that never fits ends at FSTEP_PROBE_MAX.
seedds added a commit that referenced this pull request Nov 3, 2026
…r: on the Mac the GPU clock rises for the shorter deadline over seconds, and Breath of the Wild no longer stays at 30 or 20 fps after a heavier scene (#1042)

A grace window whose second half cost at most 95% of its first and had a
late frame starts another, up to 180 grace frames in all (nvn_fstep.h
FSTEP_RAMP, FSTEP_PROBE_MAX). DM_FSTEP_PROBE_RAMP=0 turns it off.

Co-authored-by: moretti <1186409+moretti@users.noreply.github.com>
@seedds

seedds commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Landed on main as 0fd9b1c, with you credited as co-author. t_fstep passes (70 checks), and regress.py check on Breath of the Wild passes on an M4 Pro: fps p50 58.1 → 59.2, p99 33.51 → 32.9 ms, no new red lines. (That route stays at 60, so it doesn't exercise the probe itself.) Thanks!

@seedds seedds closed this Nov 3, 2026
@moretti
moretti deleted the fstep-probe-ramp branch November 8, 2026 11:44
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…r: on the Mac the GPU clock rises for the shorter deadline over seconds, and Breath of the Wild no longer stays at 30 or 20 fps after a heavier scene (seedds#1042)

A grace window whose second half cost at most 95% of its first and had a
late frame starts another, up to 180 grace frames in all (nvn_fstep.h
FSTEP_RAMP, FSTEP_PROBE_MAX). DM_FSTEP_PROBE_RAMP=0 turns it off.

Co-authored-by: moretti <1186409+moretti@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation game: 01007EF00011E000 The Legend of Zelda: Breath of the Wild 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.

2 participants