diff --git a/docs/internal/FAITHFUL_TIMING_PLAN.md b/docs/internal/FAITHFUL_TIMING_PLAN.md index 60bfcb20..41cf9d5c 100644 --- a/docs/internal/FAITHFUL_TIMING_PLAN.md +++ b/docs/internal/FAITHFUL_TIMING_PLAN.md @@ -213,6 +213,8 @@ on a fixed region -> next. ## 5. Status / Log (update every session) +- **2026-10-21 (BRIEF-4 depth state):** Removed per-batch GL state round trips, including the mask-check exception absent in the old experiment. Real-context state restoration and image/readback controls pass on Windows and Mac. Fullscreen quiet route remains unaccepted (8 completion gaps, 53.2% p99 busy). Investigating GPU query spans that include host pacing and provoke expensive dynamic-resolution reallocations. + - **2026-10-21 (BRIEF-4 Stage 1):** Reused #722 display-height cap and added opt-in `supersample_disable_at_lines`; unchanged defaults for other titles. Resolution, replay, config-parser and R4 default checks pass on Windows and macOS. R4 fullscreen 8K route cap restores 60 Hz but completion gaps remain (6 quiet / 7 loaded); no stage acceptance claimed. See R4 `analysis/STATUS.md`. - **2026-10-21 (BRIEF-4 Stage 1):** Port recorded-route measurement support from `a9da7fce`; validation pending. No guest timing changes. diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index dab81837..787990b3 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -2419,11 +2419,24 @@ static void depth_apply_ex(int mode, int tex) { } /* After the colour pass of mode 1: the depth-only pass (n vertices of the * bound buffer), then back to the default state. */ -static void depth_restore_ex(int mode, int tex, int n) { +static int depth_state_fast(void) { + static int enabled = -1; + if (enabled < 0) { + const char *e = getenv("PSX_GL_STATE_QUERY_FAST"); + enabled = !(e && e[0] == '0'); + } + return enabled; +} +static void depth_restore_ex(int mode, int tex, int n, int semi, int mask, int check) { if (mode == 0) return; if (mode == 1) { p_glUniform1f(tex ? s_tex_uZbias : s_geo_uZbias, 0.0f); - GLboolean st = glIsEnabled(GL_STENCIL_TEST), bl = glIsEnabled(GL_BLEND); + /* The preceding colour pass establishes these states. Avoid two + * synchronous queries to the threaded driver for every depth batch. */ + GLboolean st = !(tex && semi == 4 && !mask && !check), bl = semi >= 0; + if (!depth_state_fast()) { + st = glIsEnabled(GL_STENCIL_TEST); bl = glIsEnabled(GL_BLEND); + } glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_TRUE); @@ -3999,7 +4012,7 @@ static void hiw_replay_wide(void) { s_hq_v + c->vfirst, PSXGL_STREAM_DRAW); depth_apply_ex(c->depth, 1); tex_draw_passes_ex(c->vcount, c->semi, c->mask, c->check, 0, -1); - depth_restore_ex(c->depth, 1, c->vcount); + depth_restore_ex(c->depth, 1, c->vcount, c->semi, c->mask, c->check); } else { if (cur != HQ_GEO) { p_glUseProgram(s_geo_prog); @@ -4016,7 +4029,7 @@ static void hiw_replay_wide(void) { s_hq_v + c->vfirst, PSXGL_STREAM_DRAW); depth_apply_ex(c->depth, 0); BCLS_DRAW(c->semi, c->depth, c->mask, c->check, -1, glDrawArrays(GL_TRIANGLES, 0, c->vcount)); - depth_restore_ex(c->depth, 0, c->vcount); + depth_restore_ex(c->depth, 0, c->vcount, c->semi, c->mask, c->check); } } if (!any) return; @@ -4083,7 +4096,7 @@ static void hiw_flush_queue(void) { s_hq_v + c->vfirst, PSXGL_STREAM_DRAW); depth_apply_ex(c->depth, 1); tex_draw_passes_ex(c->vcount, c->semi, c->mask, c->check, 0, -1); - depth_restore_ex(c->depth, 1, c->vcount); + depth_restore_ex(c->depth, 1, c->vcount, c->semi, c->mask, c->check); } else { if (cur != HQ_GEO) { p_glUseProgram(s_geo_prog); @@ -4097,7 +4110,7 @@ static void hiw_flush_queue(void) { s_hq_v + c->vfirst, PSXGL_STREAM_DRAW); depth_apply_ex(c->depth, 0); BCLS_DRAW(c->semi, c->depth, c->mask, c->check, -1, glDrawArrays(GL_TRIANGLES, 0, c->vcount)); - depth_restore_ex(c->depth, 0, c->vcount); + depth_restore_ex(c->depth, 0, c->vcount, c->semi, c->mask, c->check); } } } @@ -4144,7 +4157,7 @@ static void flush_tex_batch_impl(void) { depth_apply_ex(dmode, 1); tex_batch_draw_passes(nverts, semi); - depth_restore_ex(dmode, 1, nverts); + depth_restore_ex(dmode, 1, nverts, semi, s_tb_mask, s_mask_check); /* Native-wide mirror — skipped for a batch fully inside the 4:3 frame (its * centre content comes from the present-time canonical blit; nothing to add @@ -4173,7 +4186,7 @@ static void flush_tex_batch_impl(void) { wide_set_bd_scale(s_tex_uXscale, s_tex_uXcenter); depth_apply_ex(dmode, 1); if (s_ws_ablate != 2) tex_batch_draw_passes(nverts, semi); - depth_restore_ex(dmode, 1, nverts); + depth_restore_ex(dmode, 1, nverts, semi, s_tb_mask, s_mask_check); wide_clear_bd_scale(s_tex_uXscale, s_tex_uXcenter); wide_target_end(s_tex_uXoff, s_tex_uXhalf); gl_perf_mirror_end(); @@ -4285,7 +4298,7 @@ static void flush_flat_batch_impl(void) { depth_apply_ex(dmode, 0); BCLS_DRAW(semi, dmode, mask, s_mask_check, b3d, if (nl) flat_batch_draw_hr_lines(nverts, nl); else glDrawArrays(fmode, 0, nverts)); - depth_restore_ex(dmode, 0, nverts); + depth_restore_ex(dmode, 0, nverts, semi, mask, s_mask_check); int mirror = g_wide_cur && !s_wide_suppress && s_ws_ablate != 1 && !(!g_ws_bd_stretch_on && mirror_flat_batch_center_only(nverts)); if (mirror) { @@ -4306,7 +4319,7 @@ static void flush_flat_batch_impl(void) { wide_set_bd_scale(s_geo_uXscale, s_geo_uXcenter); depth_apply_ex(dmode, 0); if (s_ws_ablate != 2) BCLS_DRAW(semi, dmode, mask, s_mask_check, b3d, glDrawArrays(fmode, 0, nverts)); - depth_restore_ex(dmode, 0, nverts); + depth_restore_ex(dmode, 0, nverts, semi, mask, s_mask_check); wide_clear_bd_scale(s_geo_uXscale, s_geo_uXcenter); wide_target_end(s_geo_uXoff, s_geo_uXhalf); gl_perf_mirror_end(); diff --git a/runtime/tests/test_gl_render_thread.c b/runtime/tests/test_gl_render_thread.c index 32d276e7..39a4bd77 100644 --- a/runtime/tests/test_gl_render_thread.c +++ b/runtime/tests/test_gl_render_thread.c @@ -85,6 +85,11 @@ void host_osd_present_done(void){} int psx_rewind_overlay_image(const uint32_t **p,int *w,int *h){*p=NULL;*w=*h=0;return 0;} float psx_rewind_slide(void){return 0.0f;} int psx_savestate_menu_overlay_image(const uint32_t **p,int *w,int *h){*p=NULL;*w=*h=0;return 0;} +/* No frontend menu or telemetry consumer in this renderer-only fixture. */ +int psx_host_menu_overlay_image(const uint32_t **p,int *w,int *h){*p=NULL;*w=*h=0;return 0;} +void psx_video_telemetry_compose(int r,int s,int w,int h,int dw,int dh,int vw,int vh){ + (void)r;(void)s;(void)w;(void)h;(void)dw;(void)dh;(void)vw;(void)vh;} +void psx_video_telemetry_present(int result){(void)result;} int present_shot_take(char *out,int n){(void)out;(void)n;return 0;} void present_shot_done(int ok){(void)ok;} void psx_host_sleep_ms(uint32_t ms){(void)ms;} @@ -272,6 +277,29 @@ int main(int argc, char **argv) { gl_renderer_set_swap_interval(0); if (!gl_renderer_init_context(win)) { fprintf(stderr, "FAIL context\n"); return 2; } check(gr_backend() == GR_BACKEND_OPENGL, "OpenGL backend"); + /* Compare restoration with real GL state after every colour-pass branch, + * including mask-check, dual-source blending and bloom marking. */ + for (int bloom = 0; bloom <= 1; ++bloom) + for (int tex = 0; tex <= 1; ++tex) + for (int semi = -1; semi <= (tex ? 4 : 3); ++semi) + for (int mask = 0; mask <= 1; ++mask) + for (int check_mask = 0; check_mask <= 1; ++check_mask) { + s_bcls_on = bloom; + p_glUseProgram(tex ? s_tex_prog : s_geo_prog); + p_glBindVertexArray(tex ? s_tex_vao : s_geo_vao); + depth_apply_ex(1, tex); + if (tex) tex_draw_passes_ex(0, semi, mask, check_mask, 0, -1); + else { + if (semi >= 0) apply_psx_blend(semi); else glDisable(GL_BLEND); + mask_stencil_ex(mask, check_mask); + BCLS_DRAW(semi, 1, mask, check_mask, -1, glDrawArrays(GL_TRIANGLES, 0, 0)); + } + GLboolean stencil = glIsEnabled(GL_STENCIL_TEST), blend = glIsEnabled(GL_BLEND); + depth_restore_ex(1, tex, 0, semi, mask, check_mask); + check(glIsEnabled(GL_STENCIL_TEST) == stencil, "depth pass restores stencil"); + check(glIsEnabled(GL_BLEND) == blend, "depth pass restores blend"); + } + s_bcls_on = 0; textures(); gr_wide_configure(426, 53); if (threaded) {