diff --git a/docs/WIDESCREEN.md b/docs/WIDESCREEN.md index e37909a6..ce0e22ec 100644 --- a/docs/WIDESCREEN.md +++ b/docs/WIDESCREEN.md @@ -18,12 +18,27 @@ the composite remains continuous. Tags expire after two frames and reject reused packets whose words changed. This method performs no guest writes or extra guest allocations. -OpenGL preserves the tag across flat and textured batching. While an explicitly -tagged background is active, it renders the full wide composite instead of -copying the canonical center over it, which would create a scale seam. Other -scenes retain the center-copy optimization. Measure performance in the title -and validate the visible sky at the requested aspect before release. Vulkan's -native-wide compositor is not implemented; this API does not add one. +OpenGL preserves the tag across flat and textured batching. Once a valid +background has been tagged, it renders the full wide composite instead of +copying the canonical center over it, which would create a scale seam. Packet +freshness and displayed-pixel lifetime are separate: a slow game or held display +can retain stretched pixels after the two-frame packet guard expires. The +full-composite requirement therefore remains latched until GPU reset or snapshot +restore; packet-address/content freshness is not relaxed. Sessions that have not +tagged a background retain the center-copy optimization. Measure performance in +the title and validate the visible sky at the requested aspect before release. +Vulkan's native-wide compositor is not implemented; this API does not add one. + +### Parked validation checkpoint (2026-09-12) + +The focused GPU packet/tag regression passes, including expired packets, +changed packet contents, disabled widescreen and retained composite lifetime. +The V8 Windows runtime builds and both game CTests pass. In the oil-field +21:9 playtest the owner reports that terrain looks fine and the background +flickers less badly, but still flickers. This is partial improvement, not visual +acceptance. State replay required the HLE scheduler; repeat the comparison with +matched scheduler settings before attributing the improvement solely to this +change. Work is parked in a draft; no release is approved. ## Earlier projection-and-stretch implementation diff --git a/runtime/include/gpu.h b/runtime/include/gpu.h index c7b4f491..e16f60d7 100644 --- a/runtime/include/gpu.h +++ b/runtime/include/gpu.h @@ -366,9 +366,12 @@ void gpu_ws_tag_hud_prim(uint32_t prim, int anchor); * Tag the complete background composite before submitting its ordering table. * The caller proves background identity/coverage; no guest data is modified. */ void gpu_ws_tag_background_prim(uint32_t prim); -/* Renderer policy: tagged background scenes need their full wide composite; - * copying the canonical center over it would introduce a scale discontinuity. */ +/* Fresh packet tags are eligible for background classification. */ int gpu_ws_background_stretch_active(void); +/* A tagged background can remain in a wide surface after its packet expires. + * Once used, retain full compositing until GPU reset or snapshot restoration. + * Packet freshness must never re-enable canonical-center overwrites. */ +int gpu_ws_background_requires_full_composite(void); /* Clear synthetic margins to black over an opaque 0x64/65 rectangle's Y band * immediately before it executes. Packet-guarded; canonical VRAM is untouched. */ void gpu_ws_tag_black_reveal_rect(uint32_t prim); diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index a997deab..1927ffa1 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -1967,6 +1967,15 @@ int gpu_ws_background_stretch_active(void) { frame - ws_background_tag_frame <= WS_HUD_ANCHOR_FRESH_FRAMES; } +int gpu_ws_background_requires_full_composite(void) { + /* Freshness governs reuse of a packet address, not the lifetime of the + * pixels it produced. A slow game frame or a held display buffer can keep + * those pixels on screen indefinitely. Conservatively keep full mirroring + * once a composite has been tagged; GPU reset/snapshot restore clears the + * latch together with the tags. Never modify the packet freshness guard. */ + return ws_background_tags_used && ws_native_wide_configured(); +} + static int ws_nw_explicit_background(void) { if (!gpu_ws_background_stretch_active() || gp0_cmd_source_addr == 0xFFFFFFFFu || diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index 185596c0..14815bfb 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -1705,7 +1705,7 @@ static int wide_fast_center_valid(void) { /* An explicitly stretched sky differs inside the canonical viewport too. * Keep the full composite for those scenes, including later foreground * draws. Tags are installed before DMA, so no earlier center draws skip. */ - return s_wide_fast && !gpu_ws_background_stretch_active(); + return s_wide_fast && !gpu_ws_background_requires_full_composite(); } static void wide_blit_center(GLuint wide_fbo, int base_x, int disp_y, int disp_h); /* def below */ /* True if [lo,hi] (canonical draw-x) lies strictly inside the 4:3 frame, so the diff --git a/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c b/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c index 730b040e..6f824eb2 100644 --- a/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c +++ b/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c @@ -113,11 +113,13 @@ static void test_background_packet_tags(void) { memcpy(gp0_cmd_buf, packet, sizeof(packet)); gpu_ws_tag_background_prim(addr - 4u); assert(!gpu_ws_background_stretch_active()); + assert(!gpu_ws_background_requires_full_composite()); assert(!ws_nw_explicit_background()); configure_native_wide_16_9(); gpu_ws_tag_background_prim(0x80000000u | (addr - 4u)); assert(gpu_ws_background_stretch_active()); + assert(gpu_ws_background_requires_full_composite()); assert(ws_nw_explicit_background()); /* Explicit proof remains valid after a foreground phase has begun. */ s_bg_phase_frame = (uint32_t)s_frame_count; @@ -141,11 +143,19 @@ static void test_background_packet_tags(void) { ++s_frame_count; assert(!gpu_ws_background_stretch_active()); assert(!ws_nw_explicit_background()); + /* A held display retains its stretched pixels after packet expiry. The + * compositor must not paste canonical 4:3 scenery over their center. */ + assert(gpu_ws_background_requires_full_composite()); + s_frame_count += 120; + assert(gpu_ws_background_requires_full_composite()); + assert(!ws_nw_explicit_background()); s_frame_count = 99; assert(!gpu_ws_background_stretch_active()); + assert(gpu_ws_background_requires_full_composite()); s_frame_count = 100; ws_mode = 0; assert(!gpu_ws_background_stretch_active()); + assert(!gpu_ws_background_requires_full_composite()); /* Reject semi-transparent polygons, rectangles, misalignment and RAM * overflow; a rejected tag must not even enable the full-composite path. */ @@ -153,6 +163,7 @@ static void test_background_packet_tags(void) { 0x1F800000u}; reset_gpu_state_for_test(); configure_native_wide_16_9(); + assert(!gpu_ws_background_requires_full_composite()); for (unsigned i = 0; i < sizeof(bad_prim) / sizeof(bad_prim[0]); ++i) gpu_ws_tag_background_prim(bad_prim[i]); assert(!gpu_ws_background_stretch_active());