diff --git a/runtime/include/gpu_render.h b/runtime/include/gpu_render.h index 56530cc4..26e5169a 100644 --- a/runtime/include/gpu_render.h +++ b/runtime/include/gpu_render.h @@ -53,9 +53,9 @@ void gr_set_precise_triangle(int enabled, /* Perspective-correct UV weights for the NEXT triangle ([video] * perspective_texturing). q[i] is the normalized 1/z homogeneous weight at * vertex i. enabled == 0 restores the PS1's affine UV interpolation. */ +void gr_set_perspective_triangle(int enabled, float q0, float q1, float q2); /* Original perspective-qualified triangles since process startup. */ uint32_t gr_perspective_triangle_count(void); -void gr_set_perspective_triangle(int enabled, float q0, float q1, float q2); /* Primitives */ void gr_fill_rect(int x, int y, int w, int h, uint16_t color); diff --git a/runtime/src/gpu_vk_renderer.c b/runtime/src/gpu_vk_renderer.c index cf85ebac..9c9f6e53 100644 --- a/runtime/src/gpu_vk_renderer.c +++ b/runtime/src/gpu_vk_renderer.c @@ -2790,8 +2790,8 @@ static void vkb_draw_gouraud_triangle(int x0,int y0,uint16_t c0,int x1,int y1,ui /* Full-screen-overlay wide pass (pause gray-filter / load fade): draw a flat * rect covering the FULL wide width [0, wide_w) x [y, y+h) directly into the * active wide surface (positions already wide-space, u_xoff = 0). Mirrors GL's - * wide_flat_rect_direct: full-surface scissor (the overlay must dim the - * revealed margins too). The 6 verts are consumed privately (s_vbase bumped) + * wide_flat_rect_direct: full-width scissor (the overlay must dim the + * revealed margins too), while preserving the draw-area Y band. The 6 verts are consumed privately (s_vbase bumped) * so they never join a canonical batch. */ static void wide_overlay_rect(int y, int h, uint16_t c, int semi) { if (s_wide_cur < 0 || !s_ready) return; @@ -2803,12 +2803,9 @@ static void wide_overlay_rect(int y, int h, uint16_t c, int semi) { push_vert(x0, fy0, col); push_vert(x1, fy1, col); push_vert(x0, fy1, col); s_vbase += s_vcount; s_vcount = 0; /* consume privately */ int blend = (semi < 0) ? 0 : (semi + 1); - int S = s_scale; VkCommandBuffer cb = begin_oneshot(); + /* Keep the full width and current Y clip, including interlaced row clips. */ wide_pass_begin(cb); - /* Overlay covers the whole surface: override the band scissor. */ - VkRect2D sc = { { 0, 0 }, { (uint32_t)(s_wide_w * S), (uint32_t)(VRAM_H * S) } }; - p_vkCmdSetScissor(cb, 0, 1, &sc); bind_masked(cb, 0, 0, blend, s_mask_check, s_mask_set); GeoPush gp = { px_shift(), 0.0f, (float)s_wide_w / 2.0f }; p_vkCmdPushConstants(cb, s_pl_geo, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof gp, &gp); diff --git a/runtime/tests/test_gpu_interlace_render.c b/runtime/tests/test_gpu_interlace_render.c index 58bfdc61..d4e0b54d 100644 --- a/runtime/tests/test_gpu_interlace_render.c +++ b/runtime/tests/test_gpu_interlace_render.c @@ -131,10 +131,13 @@ int main(void) { gr_set_perspective_triangle(1,1.0f,0.5f,0.25f); gr_draw_textured_triangle(2,2,0,0,10,2,8,0,2,10,0,8,0,0,0x104); CHECK(gr_perspective_triangle_count() == before + 1); + gr_set_perspective_triangle(1,0.5f,0.25f,0.125f); + gr_draw_textured_triangle(2,2,0,0,10,2,8,0,2,10,0,8,0,0,0x104); + CHECK(gr_perspective_triangle_count() == before + 2); gr_set_perspective_triangle(0,1.0f,1.0f,1.0f); gr_set_perspective_triangle(1,0.0f,1.0f,1.0f); gr_draw_textured_triangle(2,2,0,0,10,2,8,0,2,10,0,8,0,0,0x104); - CHECK(gr_perspective_triangle_count() == before + 1); + CHECK(gr_perspective_triangle_count() == before + 2); } } /* A single enhanced triangle keeps its metadata through all row clips. */ diff --git a/runtime/tests/test_vk_draw_area_batch_boundary.py b/runtime/tests/test_vk_draw_area_batch_boundary.py index 3d7c7a4d..dfadbe60 100644 --- a/runtime/tests/test_vk_draw_area_batch_boundary.py +++ b/runtime/tests/test_vk_draw_area_batch_boundary.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 from pathlib import Path import re +import sys -source = (Path(__file__).parents[1] / "src" / "gpu_vk_renderer.c").read_text() +source_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parents[1] / "src" / "gpu_vk_renderer.c" +source = source_path.read_text(encoding="utf-8") setter = re.search( r"static void vkb_set_draw_area\(.*?\n\}", source, flags=re.DOTALL ) @@ -21,3 +23,14 @@ assert tex_flush < state_write and geo_flush < state_write, ( ) print("Vulkan draw-area batch-boundary test passed") + +# Native-wide overlays must retain the same vertical clip as other geometry. +# This source guard is not a substitute for Vulkan driver/pixel qualification. +wide = re.search(r"static void wide_pass_begin\(.*?\n\}", source, flags=re.DOTALL) +overlay = re.search(r"static void wide_overlay_rect\(.*?\n\}", source, flags=re.DOTALL) +assert wide and overlay, "wide render helpers not found" +assert "s_da_y1" in wide[0] and "s_da_y2" in wide[0] +assert "p_vkCmdSetScissor" in wide[0] +assert "wide_pass_begin(cb);" in overlay[0] +assert "p_vkCmdSetScissor" not in overlay[0], "overlay overrides the draw-area Y clip" +print("Vulkan wide-overlay clip source guard passed")