diff --git a/docs/MOD_PACKAGES.md b/docs/MOD_PACKAGES.md index a0243094..f20511ce 100644 --- a/docs/MOD_PACKAGES.md +++ b/docs/MOD_PACKAGES.md @@ -541,13 +541,13 @@ offline rematch. The rest of the table is defensive, for the state listed here. | State | Setter | Reset to | When | |---|---|---|---| -| Fit / capped adaptive aspect | `psx_mod_set_adaptive_display_aspect` | off | every session | +| Fit / capped adaptive aspect | `psx_mod_set_adaptive_display_aspect` | off, cap 16:9 | every session | | World-scene predicate | `psx_mod_set_world_scene_predicate` | NULL | every session | | Retained-scene predicate | `psx_mod_set_retained_scene_predicate` | NULL | every session | | Adaptive backdrop preload | `psx_mod_set_adaptive_backdrop_preload` | 0 | every session | -| Bezel artwork | `psx_mod_set_bezel_artwork` | none | every session | +| Bezel artwork | `psx_mod_set_bezel_artwork` | none (see below) | every session | | Frame-interpolation blend mode | `psx_mod_set_frame_interpolation_blend` | default | every session | -| Native VBlank pacing | `psx_mod_set_native_vblank_rate` | off | every session | +| Native VBlank pacing and its rate | `psx_mod_set_native_vblank_rate` | off, 0 | every session | | Frame period, if native VBlank pacing was on | `psx_mod_set_native_vblank_rate` | first-session value | later sessions | | Frame interpolation and its rate | `psx_mod_set_frame_interpolation` | first-session value | later sessions | | Vsync forced off | `psx_mod_set_frame_interpolation`, `psx_mod_set_native_vblank_rate` | first-session value | later sessions | @@ -563,6 +563,12 @@ soft-returns, behaves exactly as without the reset. The RAM request matters on a rematch because `memory_init()` latches the requested geometry again at every boot, including the rematch's. +Bezel artwork has two parts. The reset clears the artwork path, so the next +session start loads nothing unless its own activation selects artwork again. +The OpenGL renderer drops the loaded texture itself when it shuts down, which +happens when a session ends by returning to the lobby (and at process exit), +so a rematch never draws the previous session's artwork in its new context. + Not reset, and why: - The **fixed display aspect** (`psx_mod_set_fixed_display_aspect`) and the diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index 45f6c6ca..7ef91e6f 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -3146,6 +3146,10 @@ void gl_renderer_shutdown(void) { if (s_ctx) { for (unsigned i = 1; i < 65536u; ++i) if (s_bank_tex[i]) glDeleteTextures(1, &s_bank_tex[i]); + /* Bezel artwork belongs to the session that loaded it: a lobby + * rematch creates a new context and loads its own artwork (or none), + * and present_bezel() must not bind this name in that context. */ + gl_renderer_set_bezel(NULL, 0, 0); } memset(s_bank_tex, 0, sizeof s_bank_tex); s_selected_bank_tex = s_tb_bank_tex = 0; diff --git a/runtime/tests/test_mod_owned_display_controls.py b/runtime/tests/test_mod_owned_display_controls.py index 369c5579..eacb0517 100644 --- a/runtime/tests/test_mod_owned_display_controls.py +++ b/runtime/tests/test_mod_owned_display_controls.py @@ -83,6 +83,22 @@ for reset in ( ): assert reset in later, f"later-session reset is missing: {reset}" +# Bezel artwork: clearing g_bezel_path only stops the next load. The loaded +# texture must not outlive the session's GL context either, or a rematch would +# bind the stale name in its new context (present_bezel() draws whenever it is +# nonzero). Only a soft return and process exit shut the renderer down. +GL = (ROOT / "runtime" / "src" / "gpu_gl_renderer.c").read_text(encoding="utf-8") +shutdown = GL[GL.index("void gl_renderer_shutdown(void) {"):] +shutdown = shutdown[:shutdown.index("\n}\n")] +live_ctx = shutdown[shutdown.index("if (s_ctx) {"):shutdown.index("SDL_GL_DeleteContext(s_ctx);")] +assert "gl_renderer_set_bezel(NULL, 0, 0);" in live_ctx, \ + "gl_renderer_shutdown() must drop the bezel texture while its context is current" +set_bezel = GL[GL.index("int gl_renderer_set_bezel(const void *rgba, int w, int h) {"):] +assert set_bezel.index("if (s_bezel_tex) { glDeleteTextures(1, &s_bezel_tex); s_bezel_tex = 0; }") < \ + set_bezel.index("if (!rgba || w <= 0 || h <= 0) return 1;"), \ + "gl_renderer_set_bezel(NULL, ...) must delete and forget the texture" +assert "if (!s_bezel_tex || ww <= 0 || wh <= 0) return;" in GL + # First-boot session block: reset immediately before activation. assert "reset_mod_owned_presentation();\n mod_runtime_activate_plugins();" in MAIN, \ "reset must run immediately before mod activation"