From 7dc34829fb70de4f2d07dc85d90f461f708f81be Mon Sep 17 00:00:00 2001 From: Alexandros Mandravillis Date: Sun, 6 Sep 2026 13:54:52 +0300 Subject: [PATCH] Synchronize depth24 clears before immediate CPU reads --- docs/GPU_GL_READBACK_REGIONS.md | 10 +++++++--- runtime/src/gpu_gl_renderer.c | 5 ++++- runtime/tests/test_gl_readback_region.c | 17 ++++++++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/docs/GPU_GL_READBACK_REGIONS.md b/docs/GPU_GL_READBACK_REGIONS.md index e7cc6a6d..091377ca 100644 --- a/docs/GPU_GL_READBACK_REGIONS.md +++ b/docs/GPU_GL_READBACK_REGIONS.md @@ -23,7 +23,10 @@ CPU-authority mode retains its existing no-readback behavior. context. It checks the complete native CPU image against full hardware readback after ordered synthetic workloads. It covers single pixels, odd-width row stride, disjoint uploads, texture dependencies, wrapping fills, overlapping -copies, masks, blending, clipping, precision margins and state restore. +copies, masks, blending, clipping, precision margins and state restore. A depth24 exit followed by an immediate +CPU read verifies that cleared movie pixels are visible and newer overlapping +texture uploads survive. It tests this existing clear policy, not full movie +playback or depth24 raster accuracy. The hardware rasterizer is shared by the comparison; this proves coherence, not independent raster accuracy. No retail assets are required. @@ -32,8 +35,9 @@ Run `python runtime/tests/run_gl_readback_region.py --compiler-bin `sdl3-src/include` and `sdl3-build/libSDL3.a`. The Windows runner uses hidden windows. It tests native and4x scales. `--gl-source --expect-unbounded` -checks the previous implementation: pixel comparisons pass, and only the small -transfer assertion fails. Each command/result is saved in `receipt.json` under a fresh run directory inside the output root. Configure `runtime/` with `BUILD_TESTING=ON` and `PSX_GL_READBACK_SDL_ROOT=` on MinGW to register `gl_readback_region_test`; run it with `ctest --test-dir -R gl_readback_region_test --output-on-failure`. Missing dependencies print a configure-time message and do not count as a hardware pass. +accepts an unbounded implementation only when pixel comparisons pass and +exactly the small-transfer assertion fails. An older implementation with a +coherence defect (including the depth24 clear) is correctly rejected too. Each command/result is saved in `receipt.json` under a fresh run directory inside the output root. Configure `runtime/` with `BUILD_TESTING=ON` and `PSX_GL_READBACK_SDL_ROOT=` on MinGW to register `gl_readback_region_test`; run it with `ctest --test-dir -R gl_readback_region_test --output-on-failure`. Missing dependencies print a configure-time message and do not count as a hardware pass. ## Prior art and scope diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index da816c38..0fa3232a 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -2544,7 +2544,10 @@ static void depth24_clear_skipped_fb(void) { glDisable(GL_SCISSOR_TEST); p_glBindFramebuffer(PSXGL_FRAMEBUFFER, 0); rect_add(&s_pack_dirty, x0, y0, x1, y1); - if (!s_cpu_auth_dual) rect_add(&s_cpu_dirty, x0, y0, x1, y1); + if (!s_cpu_auth_dual) { + rect_add(&s_cpu_dirty, x0, y0, x1, y1); + s_gpu_dirty = 1; /* The clear must reach an immediate CPU read too. */ + } present_dirty_rect(x0, y0, x1, y1, 1); rect_clear(&s_d24_skip_fb); } diff --git a/runtime/tests/test_gl_readback_region.c b/runtime/tests/test_gl_readback_region.c index 3ac74284..7dfc6e63 100644 --- a/runtime/tests/test_gl_readback_region.c +++ b/runtime/tests/test_gl_readback_region.c @@ -7,8 +7,9 @@ void gpu_vram_dirty_mark_row_impl(uint32_t y){} void gpu_vram_dirty_mark_rect(int x,int y,int w,int h){} void gpu_vram_dirty_mark_all(void){} int psx_netplay_active(void){return 0;} -int gpu_display_is_depth24(void){return 0;} -void gpu_get_display_info(GpuDisplayInfo *out){memset(out,0,sizeof(*out));} +static int test_depth24; +int gpu_display_is_depth24(void){return test_depth24;} +void gpu_get_display_info(GpuDisplayInfo *out){memset(out,0,sizeof(*out));out->display_x=32;out->display_y=32;out->width=320;out->height=16;} int psx_ws_prim_in_backdrop(void){return 0;} int g_ws_tex_edge_pct=0; int psx_ws_prim_is_tagged(void){return 0;} @@ -60,7 +61,17 @@ int main(int argc,char **argv){ glb_set_draw_area(0,0,1023,511);glb_draw_flat_rect(320,320,8,8,0x4444); for(int i=0;i<1024*512;i++)image[i]=(uint16_t)((i*23)&0x7fff); gl_renderer_restage_vram_after_savestate();verify("state restage with pending draw"); - check(image[511*1024+1020]==oracle[511*1024+1020],"edge preserved"); + /* Existing depth24 policy clears the skipped movie band on return to15-bit. + * That GPU write must become visible without waiting for another primitive. */ + static uint16_t movie[480*16], texture[4]={0x3210,0x3210,0x3210,0x3210}; + for(int i=0;i<480*16;i++)movie[i]=0x1234; + test_depth24=1;depth24_upload_policy(); + glb_vram_transfer_in(32,32,480,16,movie); + glb_vram_transfer_in(33,33,2,2,texture); + test_depth24=0;depth24_upload_policy(); + check(glb_vram_read(40,40)==0,"depth24 cleared band immediate CPU read"); + check(glb_vram_read(33,33)==0x3210,"newer overlapping texture survives clear"); + verify("depth24 leave coherence without subsequent primitive"); printf("checks=%d failures=%d\n",checks,failures); gl_renderer_shutdown();SDL_DestroyWindow(win);SDL_Quit();return failures?1:0; }