Preserve field masks in wide flat overlay geometry

This commit is contained in:
Alexandros Mandravillis
2026-09-06 14:16:53 +03:00
parent 5aee9c831a
commit 6fcfb8adbd
5 changed files with 45 additions and 19 deletions
+6
View File
@@ -48,3 +48,9 @@ integer fallback bounds and precise vertex bounds, with one row for rounding.
Tests compare native and enhanced masked pixels with unmasked images when
precise positions cross the integer bounds, including negative coordinates.
A tiny precise triangle must submit three row clips rather than 256.
Flat overlay rectangles also submit one-row geometry while field masking is
active. This keeps the mask when a wide backend intentionally replaces its
scissor. Non-interlaced overlays retain the original geometry and policy.
A backend fixture that ignores the scissor tests both field parities, original
progressive behavior, and clip restoration. This is not Vulkan driver testing.
+13 -1
View File
@@ -198,8 +198,20 @@ void gr_draw_shaded_textured_triangle(int x0, int y0, int u0, int v0, uint32_t c
GR_TRIANGLE_ROWS(gr_min(y0, gr_min(y1, y2)), gr_max(y0, gr_max(y1, y2)), g_b->draw_shaded_textured_triangle(x0, y0, u0, v0, c0, x1, y1, u1, v1, c1,
x2, y2, u2, v2, c2, clut_x, clut_y, texpage, raw));
}
/* Wide flat-overlay backends intentionally replace the clip with a full
* surface scissor. Submit row-height geometry too, so the field mask survives
* that path while ordinary full-screen overlays keep their existing policy. */
static void gr_draw_flat_rect_row(int x, int w, uint16_t c) {
int x1, y1, x2, y2;
g_b->get_draw_area(&x1, &y1, &x2, &y2);
g_b->draw_flat_rect(x, y1, w, 1, c);
}
void gr_draw_flat_rect(int x, int y, int w, int h, uint16_t c) {
GR_RASTER_ROWS(y, y + h - 1, g_b->draw_flat_rect(x, y, w, h, c));
if (gpu_raster_skipped_row() < 0) {
g_b->draw_flat_rect(x, y, w, h, c);
return;
}
GR_RASTER_ROWS(y, y + h - 1, gr_draw_flat_rect_row(x, w, c));
}
void gr_draw_textured_rect(int x, int y, int w, int h, int u, int v,
uint16_t clut_x, uint16_t clut_y, uint16_t texpage) {
+6 -3
View File
@@ -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-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)
* wide_flat_rect_direct: full-surface scissor (the overlay must dim the
* revealed margins too). 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,9 +2803,12 @@ 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);
+19 -1
View File
@@ -21,10 +21,17 @@ static void mock_triangle(int x0,int y0,int x1,int y1,int x2,int y2,uint16_t c)
++mock_submissions;if(!mock_pc || !mock_pq)++mock_bad;
mock_pc=mock_pq=0; /* GPU backends consume these per submission. */
}
/* Wide overlay paths can deliberately ignore the draw-area scissor. */
static unsigned overlay_rows[32];
static void mock_flat_overlay(int x,int y,int w,int h,uint16_t c) {
(void)x;(void)w;(void)c;
for (int row=y; row<y+h && row<32; ++row)
if (row>=0) ++overlay_rows[row];
}
static const GpuRenderBackend mock_backend={
.name="metadata fixture",.get_draw_area=sw_get_draw_area,.set_draw_area=sw_set_draw_area,
.set_precise_triangle=mock_precise,.set_perspective_triangle=mock_perspective,
.draw_flat_triangle=mock_triangle
.draw_flat_triangle=mock_triangle,.draw_flat_rect=mock_flat_overlay
};
/* This fixture keeps optional widescreen presentation disabled. */
int g_ws_bd_stretch_pct, g_ws_bd_stretch_on;
@@ -153,6 +160,17 @@ int main(void) {
gr_set_perspective_triangle(1,1.0f,1.0f,1.0f);
gr_draw_flat_triangle(0,8,1,8,0,9,0x7FFF);
CHECK(mock_submissions==3 && mock_bad==0 && !mock_pc && !mock_pq);
for (int skip=-1; skip<=1; ++skip) {
memset(overlay_rows,0,sizeof overlay_rows); skipped_row=skip;
gr_set_draw_area(0,4,31,15);
gr_draw_flat_rect(0,0,32,20,0x7FFF);
for (int y=0; y<32; ++y) {
unsigned expected = skip<0 ? y<20 : (y>=4 && y<=15 && (y&1)!=skip);
CHECK(overlay_rows[y] == expected);
}
int x1,y1,x2,y2; gr_get_draw_area(&x1,&y1,&x2,&y2);
CHECK(x1==0 && y1==4 && x2==31 && y2==15);
}
gr_set_backend(GR_BACKEND_SOFTWARE);
if (failures) return 1;
puts("PASS: field predicate, all raster primitives, clipping, transfers, mode switch, scales 1/4");
@@ -1,11 +1,9 @@
#!/usr/bin/env python3
from pathlib import Path
import re
import sys
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")
source = (Path(__file__).parents[1] / "src" / "gpu_vk_renderer.c").read_text()
setter = re.search(
r"static void vkb_set_draw_area\(.*?\n\}", source, flags=re.DOTALL
)
@@ -23,14 +21,3 @@ 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")