Merge commit 'refs/review/20261014/pr661' into integrate/pr658plus-regressions-20261014

This commit is contained in:
Matthew Stanley
2026-10-14 10:35:46 -07:00
4 changed files with 54 additions and 7 deletions
+3
View File
@@ -1821,6 +1821,9 @@ if(BUILD_TESTING)
--cc ${CMAKE_C_COMPILER})
endif()
add_test(NAME fntrace_c_linkage_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_fntrace_c_linkage.py)
add_test(NAME depth24_present_hold_vblank_test
COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tests/test_depth24_present_hold_vblank.py)
add_test(NAME netplay_load_apply_flow_test
COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_load_apply_flow.py)
+14 -3
View File
@@ -3408,6 +3408,12 @@ static uint16_t vram_write_pixels[1024 * 512];
* gpu_depth24_rgb_limit — declared early so gpu_reset_state can clear it. */
static uint32_t s_d24_upload_x1 = 0;
static int s_d24_present_hold = 0; /* vblanks to skip Swap after GP1(07h) */
/* Host side of the hold: whether the present after the latest guest VBlank
* skips its Swap. The count itself is guest state (in the GPU snap) and moves
* only at guest VBlank, so a live run, a rollback replay and a headless run
* all hold the same span (#636: a present-ticked hold stuck at 3 in headless
* live play and was zeroed by a rollback load, so replay != live). */
static int s_d24_present_skip = 0;
static uint32_t s_d24_prev_disp_h = 0; /* last GP1(07h) band height */
static void depth24_note_upload(uint32_t x, uint32_t w);
@@ -3880,6 +3886,7 @@ static void gpu_reset_state(int clear_vram) {
ws_scene_hold_reset(&s_ws_scene_hold);
s_d24_upload_x1 = 0;
s_d24_present_hold = 0;
s_d24_present_skip = 0;
s_d24_prev_disp_h = 0;
}
@@ -4145,6 +4152,9 @@ void gpu_vblank_tick(void) {
* poll first became reachable in this runtime. */
if (vertical_interlace)
interlace_field = (lcf ^ 1) & 1;
s_d24_present_skip = s_d24_present_hold > 0;
if (s_d24_present_hold > 0)
s_d24_present_hold--;
/* snapshot per-frame draw-offset-Y range for the strobe instrumentation */
if (g_doff_cnt_this) {
g_doff_min_last = g_doff_min_this;
@@ -4232,15 +4242,16 @@ void gpu_depth24_upload_span_reset(void) {
}
int gpu_depth24_present_hold_tick(void) {
if (s_d24_present_hold <= 0) return 0;
s_d24_present_hold--;
return 1;
int skip = s_d24_present_skip;
s_d24_present_skip = 0; /* one skipped Swap per guest VBlank */
return skip;
}
void gpu_depth24_on_savestate_loaded(void) {
/* Hold skips Swap — after restore we want the restored VRAM visible now.
* Upload span / prev_h were restored from the GPU snap. */
s_d24_present_hold = 0;
s_d24_present_skip = 0;
}
/* ---- Present-time screen-colour LUT (verified-enhancement, opt-in) -------
+5 -4
View File
@@ -1122,7 +1122,9 @@ extern "C" void psx_frontend_on_rb_snap_loaded(void) {
const int media = gpu_display_is_depth24() || mdec_recently_active(8) ||
cdrom_fmv_stream_pending() || cdrom_xa_stream_active();
g_audio_cycle_resync = 1;
gpu_depth24_on_savestate_loaded();
/* The depth24 present hold came back with the GPU snap and is guest state
* now (ticked at guest VBlank): keep it, so the replay matches the live
* run of the same ticks. */
s_d24_cutover_blank = 0;
if (media) {
s_d24_saw_gap = 0;
@@ -9034,9 +9036,8 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
/* Depth24 GP1(07h) retarget (MotK intro→crawl): keep the prior Swap for a
* few vblanks so stale trailing VRAM never flashes on the right edge.
* Must tick every present — gpu.c arms s_d24_present_hold and also
* freezes upload-span tracking while hold > 0; without this call the hold
* sticks and depth24_fix_trailing_margin blanks the whole FMV forever. */
* gpu.c counts the hold down at guest VBlank (it also freezes upload-span
* tracking while > 0); this skips the Swap of each held VBlank. */
if (gpu_depth24_present_hold_tick())
return ep;
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""The depth24 present hold is guest state, counted down at guest VBlank.
GP1(07h) arms s_d24_present_hold (skip a few Swaps after a depth24 band
retarget). It is in the GPU snap. It used to count down only at host present,
so a headless run never moved it, and a rollback snap load zeroed it: the
replayed save of a tick differed from the live save in the GPU section
(psxrecomp #636). Guards: gpu_vblank_tick counts it down, the present-side
query never touches it, and a rollback snap load keeps the restored value.
"""
from pathlib import Path
from test_netplay_load_apply_flow import function_body
SRC = Path(__file__).resolve().parents[1] / "src"
def main():
gpu = (SRC / "gpu.c").read_text()
vblank = function_body(gpu, "void gpu_vblank_tick(void)")
assert "s_d24_present_hold--" in vblank
tick = function_body(gpu, "int gpu_depth24_present_hold_tick(void)")
assert "s_d24_present_hold" not in tick, "the hold must not move at host present"
main_cpp = (SRC / "main.cpp").read_text()
rb_loaded = function_body(main_cpp, 'extern "C" void psx_frontend_on_rb_snap_loaded(void)')
assert "gpu_depth24_on_savestate_loaded" not in rb_loaded
print("depth24 present hold: ok")
if __name__ == "__main__":
main()