#ifdef NDEBUG #undef NDEBUG #endif /* Exercise real OT admission, complete glyph/shadow grouping and GP0 replay. * No single glyph in this counter crosses the seam. */ #include #include #include #include #define GPU_EXEC_REAL_UI_GROUP #include "../src/gpu.c" #include "gpu_exec_stubs.inc" #define HEAD 0x10000u #define ENV 0x10100u #define GLYPH 0x10200u #define GATE 0x18000u static uint32_t vertex(int x, int y) { return (uint16_t)x | ((uint32_t)(uint16_t)y << 16); } static void counter(int full_area, int tall) { memset(test_ram, 0, sizeof(test_ram)); s_frame_count = 100; ws_mode = 1; ws_full_2d = 1; ws_cfg_num = 32; ws_cfg_den = 9; ws_xnum = 3; ws_xden = 8; /* half of a 4:3 display -> 16:9 */ ws_local_viewport_cfg = 1; ws_local_viewport_slot = 0; split_recent_left_age = split_recent_right_age = 0; ws_census_on = 0; hres1 = 2; hres2 = 0; video_mode = 0; display_depth = display_disabled = display_area_x = display_area_y = 0; h_display_x1 = 0x200; h_display_x2 = 0xC00; v_display_y1 = 0x10; v_display_y2 = 0x100; draw_offset_x = draw_offset_y = 0; draw_area_left = draw_area_top = 0; draw_area_right = 511; draw_area_bottom = 239; gp0_cmd_source_addr = UINT32_MAX; gpu_ws_set_auto_ui_squash(1); gpu_ws_set_auto_ui_in_place(0); uint32_t running = 0; gpu_ws_set_local_viewport_state_gate(GATE, &running, 1); test_ram[HEAD / 4] = ENV; test_ram[ENV / 4] = (2u << 24) | GLYPH; test_ram[ENV / 4 + 1] = 0xE3000000u; test_ram[ENV / 4 + 2] = 0xE4000000u | (239u << 10) | (full_area ? 511u : 255u); const int xs[] = {224, 246, 260, 278}; for (unsigned i = 0; i < 8; i++) { unsigned addr = GLYPH + 0x40u * i; int shadow = i & 1u; int x = xs[i / 2] + shadow; int y = 12 + shadow; int h = tall ? 220 : 18; uint32_t q[] = { shadow ? 0x2E404040u : 0x2C808080u, vertex(x, y), shadow ? 0x01000000u : 0u, vertex(x + 12, y), 0x0008000Cu, vertex(x, y + h), (uint32_t)h << 8, vertex(x + 12, y + h), ((uint32_t)h << 8) | 12u, }; test_ram[addr / 4] = (9u << 24) | (i == 7 ? 0xFFFFFFu : addr + 0x40u); memcpy(&test_ram[addr / 4 + 1], q, sizeof(q)); } gpu_ws_prepass_linked_list(HEAD); assert(ws_disp_w() == 512); assert(ws_ui_prepass_count == 8); } static void load_glyph(unsigned i) { uint32_t addr = GLYPH + 0x40u * i + 4; memcpy(gp0_cmd_buf, &test_ram[addr / 4], 9 * sizeof(uint32_t)); gp0_cmd_source_addr = addr; gp0_words_needed = 9; last_scaled_rect.calls = 0; } static void expect_shared(int shared) { for (unsigned i = 0; i < 8; i++) { assert(ws_ui_prepass[i].shared_split_ui == shared); load_glyph(i); assert(ws_shared_ui_packet() == shared); } } static void width_sites(void) { counter(1,0); hres1=0; hres2=1; // 368 would not represent V82; use its 320 mode below. hres1=1; hres2=0; GpuDisplayInfo di; gpu_get_display_info(&di); assert(di.width==320); uint32_t sites[]={0x8001C248,0x8002E01C}; gpu_ws_set_local_viewport_width_sites(sites,2); assert(psx_ws_is_local_viewport_width_site(0xA001C248)); assert(!psx_ws_is_local_viewport_width_site(0x8001C24C)); for(int slot=0;slot<2;slot++) { gpu_ws_set_netplay_local_viewport(1,slot); assert(psx_ws_local_viewport_width(160)==427); assert(psx_ws_local_viewport_width(320)==320); assert(psx_ws_local_viewport_width(INT32_MAX)==INT32_MAX); assert(psx_ws_local_viewport_width(-1)==-1); } test_ram[GATE/4]=1; assert(psx_ws_local_viewport_width(160)==160); test_ram[GATE/4]=0; ws_mode=2; assert(psx_ws_local_viewport_width(160)==160); ws_mode=1; gpu_ws_set_netplay_local_viewport(0,0); assert(psx_ws_local_viewport_width(160)==160); } static void native_wide_buffers(void) { counter(1, 0); hres1 = 1; hres2 = 0; ws_mode = 2; ws_cfg_num = 16; ws_cfg_den = 9; for (int slot = 0; slot < 2; ++slot) { ws_local_viewport_slot = slot; assert(gpu_ws_netplay_local_viewport_width() == 426); for (int band = 0; band < 2; ++band) { display_area_y = 240 * (1 - band); draw_area_top = draw_offset_y = 240 * band; draw_area_bottom = draw_area_top + 239; draw_area_left = slot ? 161 : 0; draw_area_right = slot ? 319 : 159; int base = -1; assert(ws_local_viewport_draw_target(&base)); assert(base == 160 * slot); draw_area_left = slot ? 0 : 161; draw_area_right = slot ? 159 : 319; assert(!ws_local_viewport_draw_target(&base)); assert(ws_local_viewport_divider(160, 0, 161, 240, 0, 0)); assert(!ws_local_viewport_divider(160, 0, 161, 20, 0, 0)); assert(!ws_local_viewport_divider(160, 0, 161, 240, 1, 0)); assert(!ws_local_viewport_divider(160, 0, 161, 240, 0, 1)); assert(!ws_local_viewport_divider(159, 0, 163, 240, 0, 0)); } } ws_cfg_num = 21; assert(gpu_ws_netplay_local_viewport_width() == 560); ws_local_viewport_cfg = 0; assert(!ws_local_viewport_divider(160, 0, 161, 240, 0, 0)); } int main(void) { native_wide_buffers(); width_sites(); counter(1, 0); expect_shared(1); for (unsigned slot = 0; slot < 2; slot++) { ws_local_viewport_slot = (int)slot; for (unsigned i = 0; i < 8; i++) { load_glyph(i); uint32_t original[9]; memcpy(original, gp0_cmd_buf, sizeof(original)); int32_t x, y; parse_vertex(original[1], &x, &y); for (ws_shared_ui_copy = 0; ws_shared_ui_copy < 2; ws_shared_ui_copy++) { gp0_exec_textured_quad(); int base = ws_shared_ui_copy ? 256 : 0; int expected = base + 128 + ws_scale_about(x, 256) - 256; assert(last_scaled_rect.x == expected); assert(last_scaled_rect.x > base && last_scaled_rect.x + last_scaled_rect.w < base + 256); assert(last_scaled_rect.y == y); assert(last_scaled_rect.u0 == 0 && last_scaled_rect.u1 == 12); } ws_shared_ui_copy = -1; last_scaled_rect.calls = 0; gp0_execute_command(); assert(last_scaled_rect.calls == 2); assert(memcmp(original, gp0_cmd_buf, sizeof(original)) == 0); assert(ws_shared_ui_copy == -1); } } /* Wider aspects keep the complete counter inside each half. */ ws_xnum = 2; ws_xden = 7; load_glyph(0); gp0_execute_command(); assert(last_scaled_rect.calls == 2); /* Source reuse, pause, offline play and per-player clips fail closed. */ load_glyph(0); gp0_cmd_buf[1] ^= 1u; assert(!ws_shared_ui_packet()); counter(1, 0); test_ram[GATE / 4] = 1; load_glyph(0); gp0_execute_command(); assert(last_scaled_rect.calls == 1); counter(1, 0); ws_local_viewport_cfg = 0; load_glyph(0); gp0_execute_command(); assert(last_scaled_rect.calls == 1); counter(0, 0); expect_shared(0); counter(1, 1); expect_shared(0); puts("shared split HUD execution passed"); return 0; }