From a36754b7eeda86768393eb6bb55af44540ea0aa5 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Wed, 14 Oct 2026 10:24:05 -0700 Subject: [PATCH] feat(renderer): allow guarded opaque sprite backgrounds in native wide --- runtime/include/gpu.h | 7 +-- runtime/src/gpu.c | 3 +- .../test_gpu_textured_dot_nw_shift_exec.c | 54 ++++++++++++++++++- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/runtime/include/gpu.h b/runtime/include/gpu.h index fa51c73d..4ce9c929 100644 --- a/runtime/include/gpu.h +++ b/runtime/include/gpu.h @@ -495,10 +495,11 @@ void gpu_ws_set_adaptive_backdrop_preload(int enabled); * `prim` is the address of the PsyQ P_TAG word; the drawn command starts at * prim+4. anchor: -1 = left, 0 = center, +1 = right. */ void gpu_ws_tag_hud_prim(uint32_t prim, int anchor); -/* Stretch a trusted authored background polygon about the display center in +/* Stretch a trusted authored background primitive about the display center in * the native-wide output only (GL/SW). prim+4 is the command address, including - * for a command embedded inside a longer DMA packet. Only opaque polygons are - * accepted; address, full packet contents, and frame freshness are guarded. + * for a command embedded inside a longer DMA packet. Only opaque polygons and + * variable textured rectangles (64h/65h) are accepted; address, full packet + * contents, and frame freshness are guarded. * 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); diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index cc4fc219..17890e93 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -2522,7 +2522,8 @@ void gpu_ws_tag_background_prim(uint32_t prim) { return; } uint32_t op = words[0] >> 24; - if (op < 0x20u || op > 0x3Fu || (op & 2u)) { + if (!((op >= 0x20u && op <= 0x3Fu && !(op & 2u)) || + op == 0x64u || op == 0x65u)) { ws_background_tag_stats.tag_rejected++; return; } 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 31dbb239..f5306da2 100644 --- a/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c +++ b/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c @@ -143,7 +143,7 @@ static void test_background_packet_tags(void) { assert(!gpu_ws_background_stretch_active()); assert(!gpu_ws_background_requires_full_composite()); - /* Reject semi-transparent polygons, rectangles, misalignment and RAM + /* Reject semi-transparent polygons/rectangles, other commands, misalignment and RAM * overflow; a rejected tag must not even enable the full-composite path. */ const uint32_t bad_prim[] = {addr - 3u, 0xFFFFFFFCu, 0x801FFFFCu, 0x1F800000u}; @@ -153,7 +153,8 @@ static void test_background_packet_tags(void) { 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()); - const uint32_t bad_op[] = {0x2Au, 0x2Eu, 0x3Eu, 0x64u, 0x40u}; + const uint32_t bad_op[] = {0x2Au, 0x2Eu, 0x3Eu, 0x66u, 0x67u, + 0x60u, 0x6Cu, 0x74u, 0x7Cu, 0x40u}; for (unsigned i = 0; i < sizeof(bad_op) / sizeof(bad_op[0]); ++i) { test_ram[addr / 4u] = bad_op[i] << 24; gpu_ws_tag_background_prim(addr - 4u); @@ -171,6 +172,54 @@ static void test_background_packet_tags(void) { assert(!ws_nw_explicit_background()); } +static void test_background_sprite_tags(void) { + /* A compound DMA node embeds SPRT after another command, so prim+4 + * identifies SPRT without relying on a preceding P_TAG. */ + const uint32_t addr = 0x1001Cu; + for (unsigned op = 0x64u; op <= 0x65u; ++op) { + const uint32_t packet[] = {(op << 24) | 0x808080u, + pack_vertex(37, 42), 0x01230A0Bu, + (24u << 16) | 80u}; + reset_gpu_state_for_test(); + memcpy(&test_ram[addr / 4u], packet, sizeof(packet)); + configure_native_wide_16_9(); + gpu_ws_tag_background_prim(0x80000000u | (addr - 4u)); + gp0_cmd_source_addr = addr; + gp0_words_needed = 4; + memcpy(gp0_cmd_buf, packet, sizeof(packet)); + s_bg_phase_frame = (uint32_t)s_frame_count; + s_bg_phase_over = 1; + assert(psx_ws_prim_in_backdrop() == 1); + assert(gpu_ws_background_stretch_active()); + assert(gpu_ws_background_requires_full_composite()); + /* All four command words, including UV/CLUT and size, are proof. */ + for (unsigned word = 0; word < 4; ++word) { + gp0_cmd_buf[word] ^= 1u; + assert(!ws_nw_explicit_background()); + gp0_cmd_buf[word] ^= 1u; + } + gp0_cmd_source_addr += 4u; + assert(!ws_nw_explicit_background()); + gp0_cmd_source_addr -= 4u; + gp0_words_needed = 3; + assert(!ws_nw_explicit_background()); + gp0_words_needed = 4; + /* Explicit background proof reaches the renderer without changing + * the canonical sprite coordinates, dimensions or texture footprint. */ + last_textured_rect.calls = 0; + gp0_exec_textured_rect(); + assert(last_textured_rect.calls == 1); + assert(last_textured_rect.x == 37 && last_textured_rect.y == 42); + assert(last_textured_rect.w == 80 && last_textured_rect.h == 24); + assert(last_textured_rect.u == 0x0B && last_textured_rect.v == 0x0A); + assert(memcmp(&test_ram[addr / 4u], packet, sizeof(packet)) == 0); + s_frame_count += 2; + assert(ws_nw_explicit_background()); + ++s_frame_count; + assert(!ws_nw_explicit_background()); + } +} + int main(void) { reset_gpu_state_for_test(); set_dot_packet(0x10004u, 137, 42); @@ -247,6 +296,7 @@ int main(void) { } test_background_packet_tags(); + test_background_sprite_tags(); puts("gpu_textured_dot_nw_shift_exec_test: PASS (HUD and background tags)"); return 0; }