mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
runtime: keep PAL cadence off 60 Hz host sync
This commit is contained in:
@@ -585,6 +585,7 @@ if(BUILD_TESTING)
|
||||
release_zip
|
||||
overlay_decodable_fallback
|
||||
overlay_init_guard
|
||||
pal_cadence_host_refresh
|
||||
sdl3_main_single_include
|
||||
spu_sample_scheduler_default
|
||||
vk_build_default
|
||||
|
||||
@@ -501,6 +501,9 @@ if(BUILD_TESTING)
|
||||
add_test(NAME gl_depth24_upload_order_test
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_depth24_upload_order.py)
|
||||
add_test(NAME pal_cadence_host_refresh_test
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/test_pal_cadence_host_refresh.py)
|
||||
endif()
|
||||
|
||||
# Full card protocol regression: read, write, address echo, per-slot state,
|
||||
|
||||
+15
-10
@@ -2702,8 +2702,11 @@ static void apply_netplay_local_viewport_aspect(bool netplay_enabled) {
|
||||
* wall-clock pacer double-blocks the vblank callback (present is before the
|
||||
* guest resumes), which shows up as MotK FMV ~30–40 FPS in netplay vs ~50+
|
||||
* offline. Force immediate swaps for the session; restore on soft-exit. */
|
||||
static int host_refresh_is_approx_60hz(void) {
|
||||
return g_host_refresh_hz >= 58.8 && g_host_refresh_hz <= 61.2;
|
||||
static int host_refresh_matches_guest_cadence(void) {
|
||||
if (g_host_refresh_hz <= 0.0 || g_frame_period_ms <= 0.0)
|
||||
return 0;
|
||||
const double guest_hz = 1000.0 / g_frame_period_ms;
|
||||
return std::fabs(g_host_refresh_hz - guest_hz) <= guest_hz * 0.02;
|
||||
}
|
||||
|
||||
static int host_driver_vsync_unreliable(void) {
|
||||
@@ -2747,7 +2750,7 @@ static int present_vsync_owns_cadence(void) {
|
||||
return 0;
|
||||
if (g_netplay_vsync_forced_off || psx_netplay_active())
|
||||
return 0;
|
||||
return host_refresh_is_approx_60hz();
|
||||
return host_refresh_matches_guest_cadence();
|
||||
}
|
||||
|
||||
static int present_effective_swap_interval(void) {
|
||||
@@ -13257,23 +13260,25 @@ session_reboot:
|
||||
if (!g_fullscreen && !g_video_win_w_explicit)
|
||||
SDL_MaximizeWindow(sdl_window);
|
||||
|
||||
/* Host refresh: if the panel is within ~2% of 60 Hz, record it so driver
|
||||
* vsync can own cadence (pacer skipped). Non-~60 Hz and unknown refresh
|
||||
* (common on Wayland) keep PSX 59.94 Hz pacing and force swap interval 0
|
||||
* — vsync as the clock would run the sim at the panel rate. */
|
||||
/* Host refresh: if the panel matches the current guest cadence, record it
|
||||
* so driver vsync can own cadence (pacer skipped). Mismatched and unknown
|
||||
* refresh rates keep guest pacing and force swap interval 0; vsync as the
|
||||
* clock would otherwise run the sim at the panel rate. */
|
||||
{
|
||||
SDL_DisplayMode dm;
|
||||
int disp_idx = SDL_GetWindowDisplayIndex(sdl_window);
|
||||
if (disp_idx >= 0 && SDL_GetCurrentDisplayMode(disp_idx, &dm) == 0 && dm.refresh_rate > 0) {
|
||||
double host_hz = (double)dm.refresh_rate;
|
||||
g_host_refresh_hz = host_hz;
|
||||
if (host_hz >= 58.8 && host_hz <= 61.2) {
|
||||
if (host_refresh_matches_guest_cadence()) {
|
||||
g_frame_period_ms = 1000.0 / host_hz;
|
||||
std::printf("psxrecomp: sync-to-host-refresh: pacing to %.1f Hz panel "
|
||||
"(%.4f ms/frame)\n", host_hz, g_frame_period_ms);
|
||||
} else {
|
||||
std::printf("psxrecomp: host panel %.1f Hz not ~60 Hz; keeping PSX "
|
||||
"59.94 Hz pacing\n", host_hz);
|
||||
std::printf("psxrecomp: host panel %.1f Hz does not match guest "
|
||||
"cadence; keeping %.2f Hz pacing\n",
|
||||
host_hz,
|
||||
g_frame_period_ms > 0.0 ? 1000.0 / g_frame_period_ms : 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Guard PAL timing against 60 Hz host-refresh cadence leakage."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
MAIN = (ROOT / "runtime" / "src" / "main.cpp").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def require(needle: str, message: str) -> None:
|
||||
if needle not in MAIN:
|
||||
raise AssertionError(message)
|
||||
|
||||
|
||||
require(
|
||||
"static int host_refresh_matches_guest_cadence(void)",
|
||||
"host refresh must be compared against the active guest cadence",
|
||||
)
|
||||
require(
|
||||
"const double guest_hz = 1000.0 / g_frame_period_ms;",
|
||||
"guest cadence must derive from the current frame period",
|
||||
)
|
||||
require(
|
||||
"return std::fabs(g_host_refresh_hz - guest_hz) <= guest_hz * 0.02;",
|
||||
"host refresh tolerance must be relative to the current guest cadence",
|
||||
)
|
||||
require(
|
||||
"if (host_refresh_matches_guest_cadence())",
|
||||
"host-refresh sync must not overwrite PAL timing from a hard-coded 60 Hz check",
|
||||
)
|
||||
require(
|
||||
"return host_refresh_matches_guest_cadence();",
|
||||
"driver vsync cadence ownership must respect the active guest cadence",
|
||||
)
|
||||
|
||||
for forbidden in (
|
||||
"host_refresh_is_approx_60hz",
|
||||
"host_hz >= 58.8",
|
||||
"g_host_refresh_hz >= 58.8",
|
||||
"host_hz <= 61.2",
|
||||
"g_host_refresh_hz <= 61.2",
|
||||
):
|
||||
if forbidden in MAIN:
|
||||
raise AssertionError(
|
||||
f"PAL timing can leak back to the old 60 Hz-only cadence check: {forbidden}"
|
||||
)
|
||||
|
||||
print("PAL host-refresh cadence guard passed")
|
||||
Reference in New Issue
Block a user