Files
psxrecomp/runtime/include/psx_rewind.h
T
Alex Vanderveen 77095c589c runtime: make local rewind opt-in, and give it a settings key
Rewind was on for everyone and could only be turned off with PSX_REWIND=0 in
the environment. It now defaults off and is enabled from settings.toml
([video] rewind) or the launcher's Display card.

The cost it was charging silently: each snapshot is a whole machine — 2 MB main
RAM + 1 MB VRAM + 512 KB SPU RAM — serialized uncompressed into a buffer
malloc'd at a fixed 5 MiB cap that is never shrunk to the actual length, and one
is captured every rewind_interval frames (15 by default, tightening toward 4
while an FMV runs), each with a 128x96 thumbnail built pixel by pixel. At the
default depth of 50 a full ring is roughly 250 MB resident; at the maximum 200
it approaches 1 GB. That is a lot to hand a low-end host for a feature a session
may never open.

Turning it off genuinely avoids all of it: psx_rewind_configure() returns before
any allocation when rewind is not wanted, and both capture paths are guarded by
psx_rewind_enabled(). The only residue is a 450 KB BSS overlay panel whose pages
stay untouched.

The toggle applies live rather than at next launch — freeing the ring is the
whole point, so waiting for a restart would miss it. psx_rewind_shutdown()
already resets the overlay and drops a pending load, so tearing down mid-session
is safe.

PSX_REWIND still outranks the setting in both directions and now accepts 1 as
well as 0. Netplay rollback is a separate subsystem with its own ring and is
unaffected; rewind was already suppressed during netplay.

Because off is now the common case, psx_rewind_toggle() says so on the OSD
instead of leaving F8 / Select+R3 looking broken. It stays silent only in a
-DPSX_REWIND=OFF build, where there is nothing to enable.

Adds rewind_settings_test (default off, explicit off distinguishable from
absent, save/load round trip, tuning keys independent of the enable) and the
first documentation the feature has had, in config_schema.md.

Verified: recompiler tests build and pass; main.cpp type-checks against the new
recomp-ui ABI field; psx_rewind.c compiles both with and without
PSX_HAS_RBENGINE_SNAP; the existing test_rewind_toggle_combo.py still passes.
Not run end to end — this checkout has no BIOS dump to boot.

Needs recomp-ui a31ecbc for Settings.rewind_enabled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AwqKPQFnK3x7xQbK4PQKj
2026-08-28 17:33:52 -04:00

74 lines
2.8 KiB
C

#ifndef PSX_REWIND_H
#define PSX_REWIND_H
/*
* Local rewind ring + bottom filmstrip overlay (host only).
*
* Uses retcomm-rbengine snap_ring + boot_state blobs. Separate from netplay
* g_snaps. Disabled while psx_netplay_active().
*
* Keyboard: F8 (config.ini [KeyMap] Rewind). Controller: View/Back or L3.
* While open: D-pad / left-stick Left/Right, Cross load, Circle/Back close.
*
* OFF by default. The ring holds up to 200 whole-machine snapshots (2 MB RAM +
* 1 MB VRAM + 512 KB SPU RAM each) and captures one every rewind_interval
* frames, which is not a cost to hand a low-end host for a feature a session
* may never open. Turn it on in settings.toml or the launcher's Display card.
*
* Env: PSX_REWIND=1 enables, PSX_REWIND=0 disables; either outranks the UI.
* PSX_REWIND_INTERVAL (1/4/8/12/15, default 15);
* PSX_REWIND_FMV_INTERVAL (frames while depth24/MDEC/XA, default 4);
* PSX_REWIND_DEPTH (50/100/150/200, default 50, max 200).
* settings.toml [video] rewind / rewind_depth / rewind_interval (env still wins).
*/
#include <stdint.h>
#include "cpu_state.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Snap count kept in the local rewind ring. UI values: 25 / 50 / 75 / 100. */
void psx_rewind_set_depth(uint32_t depth);
void psx_rewind_set_interval(uint32_t interval);
/* Settings/launcher preference. Call before psx_rewind_configure(); to change
* it mid-session, call this then configure() (on) or shutdown() (off). */
void psx_rewind_set_enabled(int enabled);
void psx_rewind_configure(uint32_t bios_checksum, uint32_t entry_pc);
void psx_rewind_shutdown(void);
int psx_rewind_enabled(void);
int psx_rewind_is_open(void);
/* 1 while open or slide animation still visible. */
int psx_rewind_needs_present(void);
/* Guest vblank: bump frame counter / schedule capture when not paused. */
void psx_rewind_note_frame(void);
/* Safe BB-edge (beside savestate_poll): capture due snap / apply load. */
void psx_rewind_poll(CPUState *cpu, uint32_t resume_pc);
int psx_rewind_toggle(void); /* open/close; 1 if state changed */
int psx_rewind_cancel(void); /* close without load */
int psx_rewind_accept(void); /* stage load of selected snap */
void psx_rewind_move(int delta); /* -1 / +1 selection */
/* Edge-triggered nav with repeat. *_down is 1 while held. */
void psx_rewind_nav_held(int left_down, int right_down, int accept_down,
int cancel_down, uint32_t now_ms);
void psx_rewind_present_tick(uint32_t now_ms);
/* Bottom-panel ARGB8888 (little-endian). Valid until next rewind call. */
int psx_rewind_overlay_image(const uint32_t **pixels, int *w, int *h);
/* 0..1 slide (0 = off-screen below, 1 = docked). */
float psx_rewind_slide(void);
#ifdef __cplusplus
}
#endif
#endif /* PSX_REWIND_H */