feat(runtime): shorten the freeze between demo scenes

Merged
TM
20d ago
+179-26

What

Each of the real-time demo's six scenes ends by freezing its last picture for 60 frames, one second, while the next scene starts beneath it. RECOMP_DEMO_CUT_HOLD (1 to 59 frames) shortens that freeze. Unset, the game keeps its own 60 and nothing changes.

Environment variable only; no launcher has a control for it yet.

How

The demo is a script run from the block at 0x4D9688. The freeze is its instruction 0x31, with the frame count as its operand (60 in all six scenes), handled at 0x000696D0.

Seam: demo_cut_hold in recomp-runtime/title_idle.c is the manual lookup for that handler. It lowers the operand, calls the generated handler, which still owns the freeze, and puts the operand back. Only the demo's script block is touched; the lookup is not installed when the variable is unset. Recorded in docs/title-idle.md.

Verification

  • title_idle_test.c --cut-hold on synthetic memory: the handler sees the chosen count, the script keeps its 60, and another script's hold is left alone. ctest: 29 of 29 pass in a public-only configure. public_export.py verify --require-public-tree passes.
  • Scripted headless runs on an M-series Mac at 1080p (Metal): with a 12-frame hold the first cut lands about 12 frames after the freeze starts instead of 60.
  • Frame times, scripted runs with no screenshots, one demo pass each: the game's own 60 had no frame over 20 ms; a 4-frame hold had two, of 21 and 24 ms; a 1-frame hold had none in one run and six of 20 to 41 ms in another, one of them before the first cut. That is within run-to-run noise on that machine. The next scene is already drawn beneath the frozen picture.
  • Played on macOS and on a Steam Deck (Linux, Vulkan) by the author with 12-frame and 1-frame holds; no stutter noticed at either.

Not checked: frame times on the Steam Deck, Windows builds, and how the shorter demo sits against its music.