Files
psxrecomp/runtime/include/mod_session_baseline.h
T
Shokunin a87277bdad Reset mod-owned presentation state at every session start
The lobby rematch is the only in-process second session: an offline
session ends the process, and only a netplay match launched from the
lobby soft-returns (netplay_soft_exit -> psx_request_return_to_lobby).
The rematch path re-enters via goto session_reboot, below the first-boot
session block, so it reset no mod-owned state at all. The reachable leak:
a match with [netplay] local_viewport_aspect = "adaptive" leaves Fit
(g_ws_adaptive_view) on, and an offline rematch then runs adaptive
widescreen. The launcher also round-trips the match's fixed aspect
(16:9/21:9 from the local viewport) through ls.aspect_index.

reset_mod_owned_presentation() runs immediately before activation in the
first-boot session block (absorbing the existing blend reset) and in the
rematch path after its commit or netplay clear. Every call clears Fit, the
world and retained scene predicates, the adaptive backdrop preload, bezel
artwork, the blend mode and native VBlank pacing. Later calls also restore
vsync, frame interpolation and its rate, and automatic FMV skipping to
their first-session values, and clear the 8 MiB RAM request (memory_init()
re-latches it at session_reboot) and the texture-bank resolver and
batching flag. Netplay clears the plan, so no plugin has activated before
a rematch; everything past Fit is defensive. The renderer and fixed aspect
are launcher controls and process-lifetime allocations are left alone
(documented in MOD_PACKAGES.md).

The first-call capture lives in mod_session_baseline.h: its first call
records the pre-activation values (settings.toml, PSX_VSYNC, launcher) and
writes nothing back, so the first session, and every run that never
soft-returns, is unchanged. A static_assert keeps the restore honest: it
is only valid while interpolation and Skip FMVs have no launcher control.
Function-entry hooks need nothing here: the commit or netplay clear
empties the hook table and only activation rebuilds it.

The rematch path also re-applies the Settings aspect clamp (4:3 while
widescreen is mod-owned) before apply_netplay_local_viewport_aspect, so a
netplay rematch still gets its local viewport. The first-boot path keeps
the launcher/lobby aspect as before.

Not changed here: the rematch path still skips activation and the
controller, load and disc-speed resets of the first-boot block, so an
offline rematch with mods enabled applies the plan's patches and runs its
VBlank callbacks without activation.

Tests: mod_session_baseline_test (new, CTest) drives the first-call
capture and later restore with vsync on and off; it fails if the first
call writes anything. test_mod_owned_display_controls.py guards the
reset list, both call sites, reset-before-activation on the rematch path
and the rematch 4:3 re-clamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:26:33 -07:00

59 lines
2.2 KiB
C

#ifndef PSXRECOMP_MOD_SESSION_BASELINE_H
#define PSXRECOMP_MOD_SESSION_BASELINE_H
/*
* First-call capture / later-call restore for the scalar host settings a
* trusted plugin's psx_mod_* setters can change and no launcher control owns
* (vsync, frame interpolation and its rate, automatic FMV skipping, and the
* frame periods native VBlank pacing overrides). reset_mod_owned_presentation()
* in main.cpp runs this at every session start; it lives here so a unit test
* (runtime/tests/test_mod_session_baseline.c) drives the same code.
*
* The first call records the live values -- settings.toml, environment
* overrides such as PSX_VSYNC, and the launcher are resolved by then -- and
* writes nothing back, so the first session keeps exactly what the player
* configured. Later calls write that record back over the live values.
*/
typedef struct PSXModSessionScalars {
int video_vsync;
int frame_interpolation;
int frame_interpolation_fps;
int auto_skip_fmv;
double guest_frame_period_ms;
double frame_period_ms;
} PSXModSessionScalars;
typedef struct PSXModSessionBaseline {
int captured;
PSXModSessionScalars values;
} PSXModSessionBaseline;
/*
* native_vblank_forced: psx_mod_set_native_vblank_rate() owns the frame
* periods. Only then are they restored; otherwise the video-standard follower
* owns them (the guest's GP1(08h) mode), and a restore would undo a PAL/NTSC
* switch. Returns 1 when this call captured (nothing written), 0 when it
* restored.
*/
static inline int psx_mod_session_baseline_apply(PSXModSessionBaseline* base,
PSXModSessionScalars* live,
int native_vblank_forced) {
if (!base->captured) {
base->captured = 1;
base->values = *live;
return 1;
}
live->video_vsync = base->values.video_vsync;
live->frame_interpolation = base->values.frame_interpolation;
live->frame_interpolation_fps = base->values.frame_interpolation_fps;
live->auto_skip_fmv = base->values.auto_skip_fmv;
if (native_vblank_forced) {
live->guest_frame_period_ms = base->values.guest_frame_period_ms;
live->frame_period_ms = base->values.frame_period_ms;
}
return 0;
}
#endif