WIP: fix(vi): pace the 30 FPS cap in the host #609

Draft
TheDevil wants to merge 2 commits from TheDevil/Longshot:fix/30fps-host-pacing into main
First-time contributor

Type: fix · Risk: medium · Tested on: Windows, llvm-mingw game-free fixtures · Needs: in-game Windows and Linux pacing validation

Why

Players cannot select a host-paced 30 FPS limit: HOS_FPS_MAX=30 is raised to 60 on main. This extracts and corrects the frame-cap portion of #173; it does not include input, wheels, NR or the patched-runtime bridge. See #306 for the related request for lower frame limits; this change limits game frames, not additional generated frames.

How to reproduce

Seen on: production host code exercised by Windows llvm-mingw fixtures; an in-game reproduction for this revision is still pending.
Settings and switches: HOS_FPS_MAX=30; alternatively the new menu choice, with Game speed set to Per frame.
Steps: run tests/test_frame_rate_cap_30.py --cc clang and tests/test_frame_rate_cap_step.py --cc clang.
Expected: the host, game limiter and vsync period target 30; elapsed game time follows wall time; only one pacing mechanism is used.
Actual: main clamps the environment override to 60 and does not recognize the menu's new -1 choice. The combined PR's separate renderer sleep masked this rather than fixing the host.

What changed

Thirty FPS is now a choice of the existing host pacer. The engine limiter, display period and time-step hooks agree on that rate; the renderer needs no second limiter.

  • host/nn_vi.c: accept 30 from the menu and environment, keep initialization separate from the valid -1 menu value, keep interval 1 for the 30 Hz period, override Per frame at 30, and restore the normal limiter/step when returning to 60.
  • gpu/vk/fps_stats.h and the menu: matching 30 FPS graph target, existing 0–6 saved cap values unchanged, explicit help text for the real-time override, translated notices.
  • Regression fixtures cover 30/60/high-cap transitions, both override routes, single reads of the environment/ini, Per frame, and time-step integration. No per-present getenv or sleep_until is added to the renderer.

Measurements

Verdict: game-free regression checks pass; current-revision in-game A/B measurements are not yet available. The previous combined PR's renderer-sleep measurements are not reused as evidence for this host fix.

Before: main @ 2b1d38c682
After: this PR @ 74f1da1924
Hardware: Windows x64, i7-14700F, 16 GB RAM; GPU/driver not exercised by the game-free fixtures.
Scene: authored option/clock/input fixtures; no gameplay rendered for this revision.
How to reproduce: run python -I -B tests/run_all.py with the official llvm-mingw compiler on PATH; focused commands are listed under Testing.
Method: extracted production functions compiled with llvm-mingw, fake options/clock/guest state, no game or driver calls.

Check Before: main @ 2b1d38c6 After: this PR @ 74f1da19
HOS_FPS_MAX=30 host rate 60 (clamped by fps_of) 30
Menu cap -1 host rate 60 (unknown index) 30
30 FPS native pacing period no 30 Hz choice 33,333,333 ns, interval 1
Full game-free suite 530 pass, 14 fail, 34 skip 531 pass, same 14 fail, 34 skip

Flags and settings

The menu adds 30 without changing its default of 60. Existing options-file cap indices remain compatible.

Flag What it is Before this PR After this PR Effect Helps / doesn't help / untested Recommendation
frame_rate_cap Existing menu/ini key 0–6; default 0 (60) -1 adds 30; default still 0 single host cap; 30 forces real time useful for a lower render target; in-game timing still needs validation keep 60 default
HOS_FPS_MAX Existing run override 30 clamped to 60 30 accepted overrides the menu; cached once no promise of minimum FPS unset by default

Testing

Tested: Windows game-free full suite on both named revisions; focused cap, time-step, graph, environment and translation checks pass. git diff --check passes. Compiler: the installed official nightly llvm-mingw toolchain.

The identical baseline failures are: tests/test_appimage_player_build.py, tests/test_cpm_discovery.py, tests/test_crash_dialog.py, tests/test_env_on.py, tests/test_finish_output.py, tests/test_lift_cleanup_error.py, tests/test_linux_preflight.py, tests/test_make_game_cache.py, tests/test_oot_state_tap.py, tests/test_readback_late.py, tests/test_tested_configurations.py, tests/test_vk_pgo.py, tests/test_vram_budget_row.py, tests/test_walk_key.py. The before/after logs are retained locally; no game files or local paths are uploaded.

Not tested: this revision in a running game, Linux build/runtime, physical displays at different refresh rates, cutscene/audio timing and generated-frame output. WIP intentionally records these gaps rather than reusing older combined-feature results.

Must be tested:

  • Windows, supported GPU, by hand: stationary gameplay and a cutscene at menu 30 and HOS_FPS_MAX=30; alternate 30/60 twice, verify roughly 33.3/16.7 ms presents and normal game/audio time, including saved Per frame. Record two interleaved runs each at these commits, expecting correct timing/input behavior without regressions (by hand).
  • Linux, supported Vulkan GPU, by hand: build the host and repeat the same transitions, checking the native interval and absence of doubled pacing, expecting a successful native build and the same behavior (by hand).

Risks

What could break: switching the game's limiter and nominal/timed step at 30 could expose game-specific frame-count assumptions, display pacing or cutscene timing problems. HOS_GAME_INTERVAL, no-pacing and frame-generation combinations still need live runs.

Turning it off: choose an existing cap (60/default) and unset HOS_FPS_MAX; reverting this commit restores the prior selection range.

Compatibility: no save changes, no shader/cache changes, no vk ABI change. Default and saved cap indices 0–6 retain their meanings. New -1 selects 30; older builds fall back to 60 for it. The saved Game speed preference is retained and used again at 60.

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: trailers.
  • Numbers from two interleaved in-game runs and their medians: pending the runs above; WIP.
  • Switches documented and default preserved.
  • Current-revision screenshots and measured-run docs/tested/ fragment: pending live validation; WIP.
  • Game-free regression added; changed source-reading checks updated without weakening their assertions.
  • Host build/runtime on both Windows and Linux: game-free Windows fixtures passed; Linux pending.
  • Docs updated.

Upstream refresh

Current head: a9c0551156ae2043f441b4cfee05f1784fd58ed5. Main snapshot 6c9edffe is incorporated by merge, preserving published history. 5 focused checks passed after the merge; zero failures/skips. Earlier full-suite measurements remain tied to their named feature commits above. The literal rebase checklist remains unticked because history was merged, not rewritten. Fresh game/Linux checks remain WIP.

**Type:** fix · **Risk:** medium · **Tested on:** Windows, llvm-mingw game-free fixtures · **Needs:** in-game Windows and Linux pacing validation ## Why Players cannot select a host-paced 30 FPS limit: `HOS_FPS_MAX=30` is raised to 60 on main. This extracts and corrects the frame-cap portion of https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/173; it does not include input, wheels, NR or the patched-runtime bridge. See #306 for the related request for lower frame limits; this change limits game frames, not additional generated frames. ## How to reproduce **Seen on:** production host code exercised by Windows llvm-mingw fixtures; an in-game reproduction for this revision is still pending. **Settings and switches:** `HOS_FPS_MAX=30`; alternatively the new menu choice, with Game speed set to Per frame. **Steps:** run `tests/test_frame_rate_cap_30.py --cc clang` and `tests/test_frame_rate_cap_step.py --cc clang`. **Expected:** the host, game limiter and vsync period target 30; elapsed game time follows wall time; only one pacing mechanism is used. **Actual:** main clamps the environment override to 60 and does not recognize the menu's new -1 choice. The combined PR's separate renderer sleep masked this rather than fixing the host. ## What changed Thirty FPS is now a choice of the existing host pacer. The engine limiter, display period and time-step hooks agree on that rate; the renderer needs no second limiter. - `host/nn_vi.c`: accept 30 from the menu and environment, keep initialization separate from the valid -1 menu value, keep interval 1 for the 30 Hz period, override Per frame at 30, and restore the normal limiter/step when returning to 60. - `gpu/vk/fps_stats.h` and the menu: matching 30 FPS graph target, existing 0–6 saved cap values unchanged, explicit help text for the real-time override, translated notices. - Regression fixtures cover 30/60/high-cap transitions, both override routes, single reads of the environment/ini, Per frame, and time-step integration. No per-present `getenv` or `sleep_until` is added to the renderer. ## Measurements **Verdict:** game-free regression checks pass; current-revision in-game A/B measurements are not yet available. The previous combined PR's renderer-sleep measurements are not reused as evidence for this host fix. Before: main @ 2b1d38c682baaa4d52f2cacc75fffb9505c1f34f After: this PR @ 74f1da1924731d150c59f7de0270d3742a05681a Hardware: Windows x64, i7-14700F, 16 GB RAM; GPU/driver not exercised by the game-free fixtures. Scene: authored option/clock/input fixtures; no gameplay rendered for this revision. How to reproduce: run `python -I -B tests/run_all.py` with the official llvm-mingw compiler on PATH; focused commands are listed under Testing. Method: extracted production functions compiled with llvm-mingw, fake options/clock/guest state, no game or driver calls. | Check | Before: main @ 2b1d38c6 | After: this PR @ 74f1da19 | |---|---|---| | `HOS_FPS_MAX=30` host rate | 60 (clamped by `fps_of`) | 30 | | Menu cap -1 host rate | 60 (unknown index) | 30 | | 30 FPS native pacing period | no 30 Hz choice | 33,333,333 ns, interval 1 | | Full game-free suite | 530 pass, 14 fail, 34 skip | 531 pass, same 14 fail, 34 skip | ### Flags and settings The menu adds 30 without changing its default of 60. Existing options-file cap indices remain compatible. | Flag | What it is | Before this PR | After this PR | Effect | Helps / doesn't help / untested | Recommendation | |---|---|---|---|---|---|---| | frame_rate_cap | Existing menu/ini key | 0–6; default 0 (60) | -1 adds 30; default still 0 | single host cap; 30 forces real time | useful for a lower render target; in-game timing still needs validation | keep 60 default | | HOS_FPS_MAX | Existing run override | 30 clamped to 60 | 30 accepted | overrides the menu; cached once | no promise of minimum FPS | unset by default | ## Testing **Tested:** Windows game-free full suite on both named revisions; focused cap, time-step, graph, environment and translation checks pass. `git diff --check` passes. Compiler: the installed official nightly llvm-mingw toolchain. The identical baseline failures are: `tests/test_appimage_player_build.py`, `tests/test_cpm_discovery.py`, `tests/test_crash_dialog.py`, `tests/test_env_on.py`, `tests/test_finish_output.py`, `tests/test_lift_cleanup_error.py`, `tests/test_linux_preflight.py`, `tests/test_make_game_cache.py`, `tests/test_oot_state_tap.py`, `tests/test_readback_late.py`, `tests/test_tested_configurations.py`, `tests/test_vk_pgo.py`, `tests/test_vram_budget_row.py`, `tests/test_walk_key.py`. The before/after logs are retained locally; no game files or local paths are uploaded. **Not tested:** this revision in a running game, Linux build/runtime, physical displays at different refresh rates, cutscene/audio timing and generated-frame output. WIP intentionally records these gaps rather than reusing older combined-feature results. **Must be tested:** - Windows, supported GPU, by hand: stationary gameplay and a cutscene at menu 30 and `HOS_FPS_MAX=30`; alternate 30/60 twice, verify roughly 33.3/16.7 ms presents and normal game/audio time, including saved Per frame. Record two interleaved runs each at these commits, expecting correct timing/input behavior without regressions (by hand). - Linux, supported Vulkan GPU, by hand: build the host and repeat the same transitions, checking the native interval and absence of doubled pacing, expecting a successful native build and the same behavior (by hand). ## Risks **What could break:** switching the game's limiter and nominal/timed step at 30 could expose game-specific frame-count assumptions, display pacing or cutscene timing problems. `HOS_GAME_INTERVAL`, no-pacing and frame-generation combinations still need live runs. **Turning it off:** choose an existing cap (60/default) and unset `HOS_FPS_MAX`; reverting this commit restores the prior selection range. **Compatibility:** no save changes, no shader/cache changes, no vk ABI change. Default and saved cap indices 0–6 retain their meanings. New -1 selects 30; older builds fall back to 60 for it. The saved Game speed preference is retained and used again at 60. ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [ ] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` trailers. - [ ] Numbers from two interleaved in-game runs and their medians: pending the runs above; WIP. - [x] Switches documented and default preserved. - [ ] Current-revision screenshots and measured-run `docs/tested/` fragment: pending live validation; WIP. - [x] Game-free regression added; changed source-reading checks updated without weakening their assertions. - [ ] Host build/runtime on both Windows and Linux: game-free Windows fixtures passed; Linux pending. - [x] Docs updated. ## Upstream refresh Current head: `a9c0551156ae2043f441b4cfee05f1784fd58ed5`. Main snapshot `6c9edffe` is incorporated by merge, preserving published history. 5 focused checks passed after the merge; zero failures/skips. Earlier full-suite measurements remain tied to their named feature commits above. The literal rebase checklist remains unticked because history was merged, not rewritten. Fresh game/Linux checks remain WIP.
fix(vi): pace the 30 FPS cap in the host
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
74f1da1924
Assisted-by: OpenAI Codex
Author
First-time contributor

Aligned the description with the current template and named fixture results. Pending in-game/Linux validation remains explicit and the PR stays WIP.

Aligned the description with the current template and named fixture results. Pending in-game/Linux validation remains explicit and the PR stays WIP.
Merge remote-tracking branch 'origin/main' into fix/30fps-host-pacing
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
a9c0551156
Author
First-time contributor

Updated the branch to include main @ 6c9edffe by non-rewriting merge. Current head a9c0551156; 5 focused post-merge checks passed, 0 failed/skipped. Description preserves commit-specific full-suite results and remaining live/Linux validation. Still WIP.

Updated the branch to include main @ 6c9edffe by non-rewriting merge. Current head a9c0551156ae2043f441b4cfee05f1784fd58ed5; 5 focused post-merge checks passed, 0 failed/skipped. Description preserves commit-specific full-suite results and remaining live/Linux validation. Still WIP.
Member

Tested on RTX 5080, Windows 11: pass. 30 holds steadily, game time stays real, and switching between 30 and 60 in play works.

Builds. After: this PR @ a9c05511, built with make_game.py (DLSS, shaders prepared). Before: main @ 25726217 (host/nn_vi.c and gpu/vk/fps_stats.h have not changed between it and 6c9edffe). Same saves and options on both.

Method. Runs were headless with audio off and real pacing (no HOS_NO_PACING, no HOS_REALTIME_STEP), with HOS_FPS_LOG=1 and RX_VK_FRAME_LOG. I stood still in Kokiri Forest after a teleport and changed the Game frame rate cap through the control bridge (option set frame_rate_cap) every 600 presents: saved value → 30 → 60 → 30 → 60, then Game speed Per frame → 30 → 60. Main and the PR were run alternately, 3 runs each. The saved cap was 5 (240) at the start. Frame times are present intervals from the frame log, and the medians are per 600-present segment.

Before: main @ 25726217 After: this PR @ a9c05511
Lowest choice in the Game frame rate cap row 60 (option set -1 is cut to 0) 30
Menu 30, median frame time (runs 1 / 2 / 3) 16.6 ms (it stays at 60) 33.3 / 33.3 / 33.4 ms, 30.00 fps
Menu 30, p1–p99 — 30.3–36.8 ms (runs 1 and 3, both 30 segments)
Back to 60, median 16.6 ms 16.7 / 16.7 / 16.7 ms, 60.00 fps
30 with Game speed Per frame — 33.3 ms, vi: game clock speed 1.000 (real time), and Per frame still saved when back at 60
HOS_FPS_MAX=30 (2 runs each) 16.7 / 16.7 ms (60) 33.3 / 33.3 ms (30), vi: frame rate cap 30 from the start
vi: game clock speed at 30 — 0.992–1.006, median 1.000 (58 lines over 5 runs)
Saved frame_rate_cap=5 at start 240 240 (vi: frame rate cap 240)
Saved frame_rate_cap=-1 at start — 30 from the start, then 30 → 60 → 30 → 60 all correct (1 run)

Each switch logs one vi: frame rate cap 30: pacing, the game's limiter and its time step follow it line, or frame rate cap 60: the game's own limiter and step. The host is the only limiter, and nothing in the renderer sleeps. Saves and options were restored after every run and checked with diff -rq.

One PR run (run 2) had a stretch of about half a minute where the game's CPU side slowed down: the game's own frame rate fell to 21, GPU time was 6.3 ms a frame, and speed was 0.76 for one 5 s window. It started in the middle of a 30 segment and went on through the next 60 one, so it doesn't follow a cap switch. It didn't come back: run 3, with the same steps, was clean, so it was most likely other load on the PC. Run 2's other segments were 33.3 and 16.7 ms.

Our review on #173 (comment 20679), point by point:

  • Two limiters fighting: fixed. 30 is the host pacer's own period, and present_interval is 1 at 30, so the console's interval 2 doesn't halve it to 15. There is no renderer sleep, and test_frame_rate_cap_30.py checks that none is added.
  • HOS_FPS_MAX=30 still giving 60: fixed in both fps_of and fps_stats::cap_rate, and seen in game.
  • HOS_FPS_MAX read every frame: fixed. The host reads it once (the read flag also fixes main's start = -1 sentinel, which clashed with -1), and the renderer reads it once (static).
  • Game speed forced to real time without saying so: it is now said in both rows' help text, in the readme and in development.md. The saved Game speed is kept and used again at 60, which I saw in game.

Nit (not blocking): the options file loads a value under -1 as -1, since it is clamped to the row's lo, so a hand-edited frame_rate_cap=-2 gives 30 once the renderer is up. The fixture's (-2, unset) → 60 case doesn't include that clamp. No build writes such a value.

Game-free suite (python -I -B tests/run_all.py, llvm-mingw): this PR 523 pass / 16 fail / 42 skip; main 6c9edffe 522 / 16 / 42 with the same 16 failures (appimage_player_build, bake_portability, cpm_discovery, crash_dialog, env_on, finish_output, lift_cache, lift_cleanup_error, linux_preflight, make_game_cache, oot_state_tap, readback_late, tested_configurations, vk_pgo, vram_budget_row, walk_key). The extra pass is the new test_frame_rate_cap_30.py.

Not tested here: a cutscene, audio (it was off), a real display with V-Sync (the runs were headless), frame generation, HOS_GAME_INTERVAL=1, and Linux.

Tested on RTX 5080, Windows 11: **pass**. 30 holds steadily, game time stays real, and switching between 30 and 60 in play works. **Builds.** After: this PR @ a9c05511, built with `make_game.py` (DLSS, shaders prepared). Before: main @ 25726217 (`host/nn_vi.c` and `gpu/vk/fps_stats.h` have not changed between it and 6c9edffe). Same saves and options on both. **Method.** Runs were headless with audio off and real pacing (no `HOS_NO_PACING`, no `HOS_REALTIME_STEP`), with `HOS_FPS_LOG=1` and `RX_VK_FRAME_LOG`. I stood still in Kokiri Forest after a teleport and changed the Game frame rate cap through the control bridge (`option set frame_rate_cap`) every 600 presents: saved value → 30 → 60 → 30 → 60, then Game speed Per frame → 30 → 60. Main and the PR were run alternately, 3 runs each. The saved cap was 5 (240) at the start. Frame times are present intervals from the frame log, and the medians are per 600-present segment. | | Before: main @ 25726217 | After: this PR @ a9c05511 | |---|---|---| | Lowest choice in the Game frame rate cap row | 60 (`option set` -1 is cut to 0) | 30 | | Menu 30, median frame time (runs 1 / 2 / 3) | 16.6 ms (it stays at 60) | 33.3 / 33.3 / 33.4 ms, 30.00 fps | | Menu 30, p1–p99 | — | 30.3–36.8 ms (runs 1 and 3, both 30 segments) | | Back to 60, median | 16.6 ms | 16.7 / 16.7 / 16.7 ms, 60.00 fps | | 30 with Game speed Per frame | — | 33.3 ms, `vi: game clock` speed 1.000 (real time), and Per frame still saved when back at 60 | | `HOS_FPS_MAX=30` (2 runs each) | 16.7 / 16.7 ms (60) | 33.3 / 33.3 ms (30), `vi: frame rate cap 30` from the start | | `vi: game clock` speed at 30 | — | 0.992–1.006, median 1.000 (58 lines over 5 runs) | | Saved `frame_rate_cap=5` at start | 240 | 240 (`vi: frame rate cap 240`) | | Saved `frame_rate_cap=-1` at start | — | 30 from the start, then 30 → 60 → 30 → 60 all correct (1 run) | Each switch logs one `vi: frame rate cap 30: pacing, the game's limiter and its time step follow it` line, or `frame rate cap 60: the game's own limiter and step`. The host is the only limiter, and nothing in the renderer sleeps. Saves and options were restored after every run and checked with `diff -rq`. One PR run (run 2) had a stretch of about half a minute where the game's CPU side slowed down: the game's own frame rate fell to 21, GPU time was 6.3 ms a frame, and speed was 0.76 for one 5 s window. It started in the middle of a 30 segment and went on through the next 60 one, so it doesn't follow a cap switch. It didn't come back: run 3, with the same steps, was clean, so it was most likely other load on the PC. Run 2's other segments were 33.3 and 16.7 ms. **Our review on #173 (comment 20679), point by point:** - Two limiters fighting: fixed. 30 is the host pacer's own period, and `present_interval` is 1 at 30, so the console's interval 2 doesn't halve it to 15. There is no renderer sleep, and `test_frame_rate_cap_30.py` checks that none is added. - `HOS_FPS_MAX=30` still giving 60: fixed in both `fps_of` and `fps_stats::cap_rate`, and seen in game. - `HOS_FPS_MAX` read every frame: fixed. The host reads it once (the `read` flag also fixes main's `start = -1` sentinel, which clashed with -1), and the renderer reads it once (`static`). - Game speed forced to real time without saying so: it is now said in both rows' help text, in the readme and in development.md. The saved Game speed is kept and used again at 60, which I saw in game. Nit (not blocking): the options file loads a value under -1 as -1, since it is clamped to the row's `lo`, so a hand-edited `frame_rate_cap=-2` gives 30 once the renderer is up. The fixture's `(-2, unset) → 60` case doesn't include that clamp. No build writes such a value. **Game-free suite** (`python -I -B tests/run_all.py`, llvm-mingw): this PR 523 pass / 16 fail / 42 skip; main 6c9edffe 522 / 16 / 42 with the same 16 failures (appimage_player_build, bake_portability, cpm_discovery, crash_dialog, env_on, finish_output, lift_cache, lift_cleanup_error, linux_preflight, make_game_cache, oot_state_tap, readback_late, tested_configurations, vk_pgo, vram_budget_row, walk_key). The extra pass is the new `test_frame_rate_cap_30.py`. **Not tested here:** a cutscene, audio (it was off), a real display with V-Sync (the runs were headless), frame generation, `HOS_GAME_INTERVAL=1`, and Linux.
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
This pull request has changes conflicting with the target branch.
  • docs/development.md
  • host/nn_vi.c
Some workflows are waiting to be reviewed.
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'fix/30fps-host-pacing:TheDevil-fix/30fps-host-pacing'
git switch 'TheDevil-fix/30fps-host-pacing'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!609
No description provided.