mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
widescreen: auto_ui_squash corrects untextured HUD quads with their frames
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
-2
@@ -140,8 +140,10 @@ the RTPS preamble stores the anchor SXY to scratchpad `0x1F800070`.
|
||||
|
||||
`auto_ui_squash` is a runtime-only opt-in and does not require regenerated game
|
||||
code. It applies only on the projection-and-stretch path: eligible
|
||||
axis-aligned textured quads/rectangles in the front populated ordering-table
|
||||
layer are grouped by texture and screen row before any command is transformed.
|
||||
axis-aligned quads (textured, flat or gouraud) and rectangles in the front
|
||||
populated ordering-table layer are grouped by texture and screen row before any
|
||||
command is transformed. Untextured quads are included because HUD fills (health
|
||||
gradients, meter bars) sit inside textured frames and must squash with them.
|
||||
Depth-sorted world packets, full-frame backdrops, and true 4:3 frames remain
|
||||
untouched.
|
||||
|
||||
|
||||
@@ -417,6 +417,23 @@ if(BUILD_TESTING)
|
||||
add_test(NAME gpu_textured_dot_nw_shift_exec_test
|
||||
COMMAND gpu_textured_dot_nw_shift_exec_test)
|
||||
|
||||
# auto_ui_squash corrects untextured HUD fills with their textured frames.
|
||||
add_executable(ws_auto_ui_untextured_exec_test
|
||||
tests/test_ws_auto_ui_untextured_exec.c
|
||||
src/ws_ui_group.c
|
||||
src/psx_video_timing.c)
|
||||
target_include_directories(ws_auto_ui_untextured_exec_test PRIVATE
|
||||
include
|
||||
src)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(ws_auto_ui_untextured_exec_test PRIVATE
|
||||
-ffunction-sections -fdata-sections)
|
||||
target_link_options(ws_auto_ui_untextured_exec_test PRIVATE
|
||||
-Wl,--gc-sections)
|
||||
endif()
|
||||
add_test(NAME ws_auto_ui_untextured_exec_test
|
||||
COMMAND ws_auto_ui_untextured_exec_test)
|
||||
|
||||
add_executable(ws_view_anchor_test tests/test_ws_view_anchor.c)
|
||||
add_executable(ws_view_anchor_gpu_test tests/test_ws_view_anchor_gpu.c
|
||||
src/psx_video_timing.c)
|
||||
|
||||
+18
-5
@@ -2518,7 +2518,8 @@ static int ws_auto_ui_anchor(int32_t *out_anchor) {
|
||||
static uint32_t ws_auto_ui_group_key_words(const uint32_t *words,
|
||||
uint32_t op,
|
||||
int32_t y, int32_t h) {
|
||||
uint32_t clut = words[2] >> 16;
|
||||
/* An untextured polygon's word 2 is a vertex or colour, not a CLUT. */
|
||||
uint32_t clut = (op < 0x60u && !(op & 0x04u)) ? 0u : words[2] >> 16;
|
||||
uint32_t tpage = 0;
|
||||
if (op >= 0x20u && op <= 0x3Fu && (op & 0x04u)) {
|
||||
int tp_index = (op & 0x10u) ? 5 : 4;
|
||||
@@ -4164,6 +4165,7 @@ static void gp0_exec_mono_quad(void) {
|
||||
int rej_a = psx_gpu_triangle_oversize(vx, vy, 0, 1, 2);
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
ws_auto_ui_transform_quad(vx, vy);
|
||||
|
||||
/* Each added band is outside the original screen and executes in the
|
||||
* mask's own OT position/blend mode. Canonical VRAM clips it away. */
|
||||
@@ -4286,6 +4288,7 @@ static void gp0_exec_shaded_quad(void) {
|
||||
int rej_a = psx_gpu_triangle_oversize(vx, vy, 0, 1, 2);
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
ws_auto_ui_transform_quad(vx, vy);
|
||||
ws_nw_backdrop_stretch_quad(vx, vy); /* full-frame 2D backdrop stretch (sky gradient; no-op else) */
|
||||
ws_nw_hud_shift_vertices(vx, 4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -5267,15 +5270,25 @@ static void ws_ui_prepass_add(const uint32_t *words, uint32_t word_count,
|
||||
uint32_t op = words[0] >> 24;
|
||||
int32_t min_x, max_x, min_y, max_y;
|
||||
|
||||
if (op >= 0x20u && op <= 0x3Fu && (op & 0x04u) &&
|
||||
(op & 0x08u)) {
|
||||
if (op >= 0x20u && op <= 0x3Fu && (op & 0x08u)) {
|
||||
/* Any 4-vertex polygon. Untextured HUD fills (health gradients,
|
||||
* meter bars) sit in the same front layer as the textured frames
|
||||
* around them; leaving them out kept their raw 4:3 X while the frame
|
||||
* squashed, so the fill overhung it at wide aspects (Spider-Man's
|
||||
* health and webbing meters). Same rank + axis-aligned gate as the
|
||||
* textured quads. */
|
||||
const int textured = (op & 0x04u) != 0;
|
||||
const int shaded = (op & 0x10u) != 0;
|
||||
int indices[4];
|
||||
if (op & 0x10u) {
|
||||
if (textured && shaded) {
|
||||
indices[0] = 1; indices[1] = 4;
|
||||
indices[2] = 7; indices[3] = 10;
|
||||
} else {
|
||||
} else if (textured || shaded) {
|
||||
indices[0] = 1; indices[1] = 3;
|
||||
indices[2] = 5; indices[3] = 7;
|
||||
} else {
|
||||
indices[0] = 1; indices[1] = 2;
|
||||
indices[2] = 3; indices[3] = 4;
|
||||
}
|
||||
int32_t vx[4], vy[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
/* Shared link stubs for tests that compile runtime/src/gpu.c directly and
|
||||
* execute GP0 packets without a GL context, mod bank or emulator core.
|
||||
* Include right after "../src/gpu.c". Define GPU_EXEC_REAL_UI_GROUP before
|
||||
* including to link the real ws_ui_group.c instead of the grouping stub.
|
||||
* Draw calls that tests observe are recorded below. */
|
||||
|
||||
uint64_t s_frame_count;
|
||||
int g_exec_phase;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
CPUState *debug_cpu_ptr;
|
||||
int g_psx_vram_dirty_tracking;
|
||||
uint32_t g_psx_ram_size = 0x00200000u;
|
||||
uint32_t g_psx_ram_mask = 0x001FFFFFu;
|
||||
|
||||
/* This fixture exercises stock GPU packets without a GL context or mod bank.
|
||||
* Keep the gpu.c dependencies explicit, including on linkers that retain
|
||||
* additional exported command handlers despite --gc-sections. */
|
||||
GrBackend gr_backend(void) { return GR_BACKEND_SOFTWARE; }
|
||||
int gl_renderer_texture_banks_supported(void) { return 0; }
|
||||
int gl_renderer_select_texture_bank(uint16_t id) { return id == 0; }
|
||||
int gl_renderer_select_texture_bank_live_clut(uint16_t id) { return id == 0; }
|
||||
uint16_t mod_texture_packet_bank(uint32_t source, const uint32_t *words,
|
||||
uint32_t count) {
|
||||
(void)source; (void)words; (void)count;
|
||||
return 0;
|
||||
}
|
||||
int mod_texture_packet_precision(uint32_t source, float q[3], float xy[6]) {
|
||||
(void)source; (void)q; (void)xy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t test_ram[0x00200000u / 4u];
|
||||
|
||||
static struct {
|
||||
int calls;
|
||||
int x, y, w, h;
|
||||
int u, v;
|
||||
uint16_t clut_x, clut_y, texpage;
|
||||
} last_textured_rect;
|
||||
static struct { int calls, x, y, w, h, u0, v0, u1, v1; } last_scaled_rect;
|
||||
|
||||
/* Bounding box of every flat/gouraud triangle drawn since the last reset. */
|
||||
static struct { int calls, min_x, max_x; } gpu_exec_triangles;
|
||||
static inline void gpu_exec_reset_triangles(void) {
|
||||
gpu_exec_triangles.calls = 0;
|
||||
gpu_exec_triangles.min_x = 1 << 30;
|
||||
gpu_exec_triangles.max_x = -(1 << 30);
|
||||
}
|
||||
static inline void gpu_exec_record_triangle(int x0, int y0, int x1, int y1,
|
||||
int x2, int y2) {
|
||||
const int xs[3] = {x0, x1, x2};
|
||||
(void)y0; (void)y1; (void)y2;
|
||||
gpu_exec_triangles.calls++;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (xs[i] < gpu_exec_triangles.min_x) gpu_exec_triangles.min_x = xs[i];
|
||||
if (xs[i] > gpu_exec_triangles.max_x) gpu_exec_triangles.max_x = xs[i];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t psx_read_word(uint32_t addr) {
|
||||
uint32_t phys = addr & 0x1FFFFFFFu;
|
||||
if (phys <= 0x00200000u - 4u)
|
||||
return test_ram[phys / 4u];
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t psx_read_half(uint32_t addr) {
|
||||
uint32_t word = psx_read_word(addr & ~3u);
|
||||
return (uint16_t)(word >> ((addr & 2u) ? 16 : 0));
|
||||
}
|
||||
|
||||
uint8_t psx_read_byte(uint32_t addr) {
|
||||
uint32_t word = psx_read_word(addr & ~3u);
|
||||
return (uint8_t)(word >> ((addr & 3u) * 8u));
|
||||
}
|
||||
|
||||
void psx_write_half(uint32_t addr, uint16_t val) {
|
||||
(void)addr;
|
||||
(void)val;
|
||||
}
|
||||
|
||||
void psx_host_write_half(uint32_t addr, uint16_t val) {
|
||||
psx_write_half(addr, val);
|
||||
}
|
||||
|
||||
int mdec_recently_active(uint32_t within_frames) {
|
||||
(void)within_frames;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psx_fatal_halt(const char *reason) {
|
||||
(void)reason;
|
||||
assert(!"psx_fatal_halt called");
|
||||
}
|
||||
|
||||
void pgxp_set_enabled(int enabled) { (void)enabled; }
|
||||
int gte_geometry_correction_enabled(void) { return 0; }
|
||||
int pgxp_get_precise_vertex(uint32_t addr, uint32_t packet_word,
|
||||
int32_t int_x, int32_t int_y,
|
||||
int32_t *x16, int32_t *y16, uint16_t *sz) {
|
||||
(void)addr; (void)packet_word; (void)int_x; (void)int_y;
|
||||
(void)x16; (void)y16; (void)sz;
|
||||
return PGXP_SRC_NATIVE;
|
||||
}
|
||||
int gte_precision_load_word(uint32_t addr, uint32_t packed,
|
||||
int32_t *x16, int32_t *y16, uint16_t *z) {
|
||||
(void)addr; (void)packed; (void)x16; (void)y16; (void)z;
|
||||
return 0;
|
||||
}
|
||||
uint32_t sw_perspective_triangle_count(void) { return 0; }
|
||||
|
||||
uint32_t psx_ws_widen_angle_q12(uint32_t vanilla, int extent_pixels) {
|
||||
(void)extent_pixels;
|
||||
return vanilla;
|
||||
}
|
||||
int psx_ws_aspect_cone_contains(int32_t x, int32_t z, int32_t y,
|
||||
int32_t fx, int32_t fz, int32_t fy,
|
||||
uint32_t threshold, int extent_pixels) {
|
||||
(void)x; (void)z; (void)y; (void)fx; (void)fz; (void)fy;
|
||||
(void)threshold; (void)extent_pixels;
|
||||
return 0;
|
||||
}
|
||||
#ifndef GPU_EXEC_REAL_UI_GROUP
|
||||
void ws_ui_group_assign(WsUiGroupItem *items, size_t count,
|
||||
int32_t display_width, int dense_menu, int in_place) {
|
||||
(void)items; (void)count; (void)display_width; (void)dense_menu;
|
||||
(void)in_place;
|
||||
}
|
||||
int32_t ws_ui_anchor_for_bounds(int32_t x, int32_t width,
|
||||
int32_t display_width) {
|
||||
(void)width;
|
||||
if (x * 3 < display_width) return -1;
|
||||
if (x * 3 > display_width * 2) return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void gpu_vram_dirty_mark_all(void) {}
|
||||
void gpu_vram_dirty_mark_row_impl(uint32_t y) { (void)y; }
|
||||
void psx_irq_raise(uint32_t bit, uint32_t detail) { (void)bit; (void)detail; }
|
||||
void event_ring_record_aux(uint16_t kind, uint8_t detail, uint32_t aux) {
|
||||
(void)kind; (void)detail; (void)aux;
|
||||
}
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
int psx_netplay_active(void) { return 0; }
|
||||
int sio_hold_present_for_card(void) { return 0; }
|
||||
uint32_t psx_compiled_irq_resume_pc(void) { return 0; }
|
||||
uint32_t psx_last_irq_check_pc(void) { return 0; }
|
||||
uint32_t psx_netplay_rb_sticky_bb_pc(void) { return 0; }
|
||||
void mod_runtime_on_vblank(void) {}
|
||||
bool screen_kind_from_name(const char *name, ScreenKind *out) {
|
||||
(void)name;
|
||||
if (out) *out = SCREEN_RAW;
|
||||
return true;
|
||||
}
|
||||
ColorLut *color_lut_create(const ColorSettings *settings) {
|
||||
(void)settings;
|
||||
return NULL;
|
||||
}
|
||||
void color_lut_destroy(ColorLut *lut) { (void)lut; }
|
||||
bool color_lut_is_passthrough(const ColorLut *lut) {
|
||||
(void)lut;
|
||||
return true;
|
||||
}
|
||||
void color_lut_map555(const ColorLut *lut, uint16_t bgr555,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b) {
|
||||
(void)lut;
|
||||
if (r) *r = (uint8_t)((bgr555 & 0x1Fu) << 3);
|
||||
if (g) *g = (uint8_t)(((bgr555 >> 5) & 0x1Fu) << 3);
|
||||
if (b) *b = (uint8_t)(((bgr555 >> 10) & 0x1Fu) << 3);
|
||||
}
|
||||
uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) { return address; }
|
||||
void text_xlate_vram_upload(int x, int y, int w, int h) {
|
||||
(void)x; (void)y; (void)w; (void)h;
|
||||
}
|
||||
uint32_t debug_guest_ra(void) { return 0; }
|
||||
uint32_t debug_guest_sp(void) { return 0; }
|
||||
uint8_t *memory_get_ram_ptr(void) { return (uint8_t *)test_ram; }
|
||||
|
||||
void gr_init(uint16_t *vram_ptr) { (void)vram_ptr; }
|
||||
void gr_set_scale(int scale) { (void)scale; }
|
||||
int gr_scale(void) { return 1; }
|
||||
void gr_set_texture_filter(int bilinear) { (void)bilinear; }
|
||||
int gr_texture_filter(void) { return 0; }
|
||||
void gr_set_semi_transparency(int enabled, int mode) { (void)enabled; (void)mode; }
|
||||
void gr_set_mask_bits(int set_bit, int check_bit) { (void)set_bit; (void)check_bit; }
|
||||
void gr_set_texture_window(uint32_t raw) { (void)raw; }
|
||||
void gr_set_color_modulation(int r, int g, int b, int raw_texture) {
|
||||
(void)r; (void)g; (void)b; (void)raw_texture;
|
||||
}
|
||||
void gr_set_precise_triangle(int enabled, int32_t x0, int32_t y0,
|
||||
int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
|
||||
(void)enabled; (void)x0; (void)y0; (void)x1; (void)y1; (void)x2; (void)y2;
|
||||
}
|
||||
void gr_set_perspective_triangle(int enabled, float q0, float q1, float q2) {
|
||||
(void)enabled; (void)q0; (void)q1; (void)q2;
|
||||
}
|
||||
void gr_fill_rect(int x, int y, int w, int h, uint16_t color) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)color;
|
||||
}
|
||||
void gr_copy_rect(int src_x, int src_y, int dst_x, int dst_y, int w, int h) {
|
||||
(void)src_x; (void)src_y; (void)dst_x; (void)dst_y; (void)w; (void)h;
|
||||
}
|
||||
void gr_draw_flat_triangle(int x0, int y0, int x1, int y1, int x2, int y2,
|
||||
uint16_t color) {
|
||||
gpu_exec_record_triangle(x0, y0, x1, y1, x2, y2);
|
||||
(void)color;
|
||||
}
|
||||
void gr_draw_gouraud_triangle(int x0, int y0, uint16_t c0,
|
||||
int x1, int y1, uint16_t c1,
|
||||
int x2, int y2, uint16_t c2) {
|
||||
gpu_exec_record_triangle(x0, y0, x1, y1, x2, y2);
|
||||
(void)c0; (void)c1; (void)c2;
|
||||
}
|
||||
void gr_draw_textured_triangle(int x0, int y0, int u0, int v0,
|
||||
int x1, int y1, int u1, int v1,
|
||||
int x2, int y2, int u2, int v2,
|
||||
uint16_t clut_x, uint16_t clut_y,
|
||||
uint16_t texpage) {
|
||||
(void)x0; (void)y0; (void)u0; (void)v0; (void)x1; (void)y1; (void)u1;
|
||||
(void)v1; (void)x2; (void)y2; (void)u2; (void)v2; (void)clut_x;
|
||||
(void)clut_y; (void)texpage;
|
||||
}
|
||||
void gr_draw_shaded_textured_triangle(int x0, int y0, int u0, int v0,
|
||||
uint32_t color0, int x1, int y1,
|
||||
int u1, int v1, uint32_t color1,
|
||||
int x2, int y2, int u2, int v2,
|
||||
uint32_t color2, uint16_t clut_x,
|
||||
uint16_t clut_y, uint16_t texpage,
|
||||
int raw_texture) {
|
||||
(void)x0; (void)y0; (void)u0; (void)v0; (void)color0; (void)x1;
|
||||
(void)y1; (void)u1; (void)v1; (void)color1; (void)x2; (void)y2;
|
||||
(void)u2; (void)v2; (void)color2; (void)clut_x; (void)clut_y;
|
||||
(void)texpage; (void)raw_texture;
|
||||
}
|
||||
void gr_draw_flat_rect(int x, int y, int w, int h, uint16_t color) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)color;
|
||||
}
|
||||
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) {
|
||||
last_textured_rect.calls++;
|
||||
last_textured_rect.x = x;
|
||||
last_textured_rect.y = y;
|
||||
last_textured_rect.w = w;
|
||||
last_textured_rect.h = h;
|
||||
last_textured_rect.u = u;
|
||||
last_textured_rect.v = v;
|
||||
last_textured_rect.clut_x = clut_x;
|
||||
last_textured_rect.clut_y = clut_y;
|
||||
last_textured_rect.texpage = texpage;
|
||||
}
|
||||
void gr_draw_textured_rect_scaled(int x, int y, int w, int h,
|
||||
int u0, int v0, int u1, int v1,
|
||||
uint16_t clut_x, uint16_t clut_y,
|
||||
uint16_t texpage) {
|
||||
last_scaled_rect.calls++;
|
||||
last_scaled_rect.x=x; last_scaled_rect.y=y;
|
||||
last_scaled_rect.w=w; last_scaled_rect.h=h;
|
||||
last_scaled_rect.u0=u0; last_scaled_rect.v0=v0;
|
||||
last_scaled_rect.u1=u1; last_scaled_rect.v1=v1;
|
||||
(void)v1; (void)clut_x; (void)clut_y; (void)texpage;
|
||||
}
|
||||
void gr_draw_line(int x0, int y0, int x1, int y1, uint16_t color) {
|
||||
(void)x0; (void)y0; (void)x1; (void)y1; (void)color;
|
||||
}
|
||||
void gr_draw_shaded_line(int x0, int y0, uint16_t c0,
|
||||
int x1, int y1, uint16_t c1) {
|
||||
(void)x0; (void)y0; (void)c0; (void)x1; (void)y1; (void)c1;
|
||||
}
|
||||
int gr_render_display(uint32_t *out, int pitch, int dx, int dy, int dw, int dh) {
|
||||
(void)out; (void)pitch; (void)dx; (void)dy; (void)dw; (void)dh;
|
||||
return 0;
|
||||
}
|
||||
int gr_render_display_hires(uint32_t *out, int pitch, int dx, int dy, int dw, int dh) {
|
||||
(void)out; (void)pitch; (void)dx; (void)dy; (void)dw; (void)dh;
|
||||
return 0;
|
||||
}
|
||||
void gr_vram_write(int x, int y, uint16_t pixel) { (void)x; (void)y; (void)pixel; }
|
||||
uint16_t gr_vram_read(int x, int y) { (void)x; (void)y; return 0; }
|
||||
void gr_vram_transfer_in(int x, int y, int w, int h, const uint16_t *data) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)data;
|
||||
}
|
||||
void gr_vram_transfer_out(int x, int y, int w, int h, uint16_t *data) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)data;
|
||||
}
|
||||
void gr_set_draw_area(int x1, int y1, int x2, int y2) {
|
||||
(void)x1; (void)y1; (void)x2; (void)y2;
|
||||
}
|
||||
void gr_get_draw_area(int *x1, int *y1, int *x2, int *y2) {
|
||||
if (x1) *x1 = (int)draw_area_left;
|
||||
if (y1) *y1 = (int)draw_area_top;
|
||||
if (x2) *x2 = (int)draw_area_right;
|
||||
if (y2) *y2 = (int)draw_area_bottom;
|
||||
}
|
||||
void gr_set_draw_offset(int x, int y) { (void)x; (void)y; }
|
||||
int gr_wide_supported(void) { return 1; }
|
||||
void gr_wide_configure(int wide_w, int offset) { (void)wide_w; (void)offset; }
|
||||
void gr_wide_set_view(int enabled, int shift, int left, int right) {
|
||||
(void)enabled; (void)shift; (void)left; (void)right;
|
||||
}
|
||||
void gr_wide_set_target(int base_x) { (void)base_x; }
|
||||
void gr_wide_disable_target(void) {}
|
||||
void gr_wide_clear(int base_x, int y, int h, uint16_t color) {
|
||||
(void)base_x; (void)y; (void)h; (void)color;
|
||||
}
|
||||
void gr_wide_clear_margins(int base_x, int y, int h, uint16_t color, int sides) {
|
||||
(void)base_x; (void)y; (void)h; (void)color; (void)sides;
|
||||
}
|
||||
int gr_wide_dump_full(uint32_t *out, int cap_pixels, int *ow, int *oh, int base_x) {
|
||||
(void)out; (void)cap_pixels; (void)ow; (void)oh; (void)base_x;
|
||||
return 0;
|
||||
}
|
||||
int gr_render_wide_display(uint32_t *out, int pitch, int base_x,
|
||||
int disp_y, int disp_h) {
|
||||
(void)out; (void)pitch; (void)base_x; (void)disp_y; (void)disp_h;
|
||||
return 0;
|
||||
}
|
||||
@@ -9,41 +9,7 @@
|
||||
|
||||
#include "../src/gpu.c"
|
||||
|
||||
uint64_t s_frame_count;
|
||||
int g_exec_phase;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
CPUState *debug_cpu_ptr;
|
||||
int g_psx_vram_dirty_tracking;
|
||||
uint32_t g_psx_ram_size = 0x00200000u;
|
||||
uint32_t g_psx_ram_mask = 0x001FFFFFu;
|
||||
|
||||
/* This fixture exercises stock GPU packets without a GL context or mod bank.
|
||||
* Keep the gpu.c dependencies explicit, including on linkers that retain
|
||||
* additional exported command handlers despite --gc-sections. */
|
||||
GrBackend gr_backend(void) { return GR_BACKEND_SOFTWARE; }
|
||||
int gl_renderer_texture_banks_supported(void) { return 0; }
|
||||
int gl_renderer_select_texture_bank(uint16_t id) { return id == 0; }
|
||||
int gl_renderer_select_texture_bank_live_clut(uint16_t id) { return id == 0; }
|
||||
uint16_t mod_texture_packet_bank(uint32_t source, const uint32_t *words,
|
||||
uint32_t count) {
|
||||
(void)source; (void)words; (void)count;
|
||||
return 0;
|
||||
}
|
||||
int mod_texture_packet_precision(uint32_t source, float q[3], float xy[6]) {
|
||||
(void)source; (void)q; (void)xy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t test_ram[0x00200000u / 4u];
|
||||
|
||||
static struct {
|
||||
int calls;
|
||||
int x, y, w, h;
|
||||
int u, v;
|
||||
uint16_t clut_x, clut_y, texpage;
|
||||
} last_textured_rect;
|
||||
static struct { int calls, x, y, w, h, u0, v0, u1, v1; } last_scaled_rect;
|
||||
#include "gpu_exec_stubs.inc"
|
||||
|
||||
static uint32_t pack_vertex(int16_t x, int16_t y) {
|
||||
return (uint16_t)x | ((uint32_t)(uint16_t)y << 16);
|
||||
@@ -143,260 +109,3 @@ int main(void) {
|
||||
puts("gpu_textured_dot_nw_shift_exec_test: PASS");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t psx_read_word(uint32_t addr) {
|
||||
uint32_t phys = addr & 0x1FFFFFFFu;
|
||||
if (phys <= 0x00200000u - 4u)
|
||||
return test_ram[phys / 4u];
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t psx_read_half(uint32_t addr) {
|
||||
uint32_t word = psx_read_word(addr & ~3u);
|
||||
return (uint16_t)(word >> ((addr & 2u) ? 16 : 0));
|
||||
}
|
||||
|
||||
uint8_t psx_read_byte(uint32_t addr) {
|
||||
uint32_t word = psx_read_word(addr & ~3u);
|
||||
return (uint8_t)(word >> ((addr & 3u) * 8u));
|
||||
}
|
||||
|
||||
void psx_write_half(uint32_t addr, uint16_t val) {
|
||||
(void)addr;
|
||||
(void)val;
|
||||
}
|
||||
|
||||
void psx_host_write_half(uint32_t addr, uint16_t val) {
|
||||
psx_write_half(addr, val);
|
||||
}
|
||||
|
||||
int mdec_recently_active(uint32_t within_frames) {
|
||||
(void)within_frames;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psx_fatal_halt(const char *reason) {
|
||||
(void)reason;
|
||||
assert(!"psx_fatal_halt called");
|
||||
}
|
||||
|
||||
void pgxp_set_enabled(int enabled) { (void)enabled; }
|
||||
int gte_geometry_correction_enabled(void) { return 0; }
|
||||
int pgxp_get_precise_vertex(uint32_t addr, uint32_t packet_word,
|
||||
int32_t int_x, int32_t int_y,
|
||||
int32_t *x16, int32_t *y16, uint16_t *sz) {
|
||||
(void)addr; (void)packet_word; (void)int_x; (void)int_y;
|
||||
(void)x16; (void)y16; (void)sz;
|
||||
return PGXP_SRC_NATIVE;
|
||||
}
|
||||
int gte_precision_load_word(uint32_t addr, uint32_t packed,
|
||||
int32_t *x16, int32_t *y16, uint16_t *z) {
|
||||
(void)addr; (void)packed; (void)x16; (void)y16; (void)z;
|
||||
return 0;
|
||||
}
|
||||
uint32_t sw_perspective_triangle_count(void) { return 0; }
|
||||
|
||||
uint32_t psx_ws_widen_angle_q12(uint32_t vanilla, int extent_pixels) {
|
||||
(void)extent_pixels;
|
||||
return vanilla;
|
||||
}
|
||||
int psx_ws_aspect_cone_contains(int32_t x, int32_t z, int32_t y,
|
||||
int32_t fx, int32_t fz, int32_t fy,
|
||||
uint32_t threshold, int extent_pixels) {
|
||||
(void)x; (void)z; (void)y; (void)fx; (void)fz; (void)fy;
|
||||
(void)threshold; (void)extent_pixels;
|
||||
return 0;
|
||||
}
|
||||
void ws_ui_group_assign(WsUiGroupItem *items, size_t count,
|
||||
int32_t display_width, int dense_menu, int in_place) {
|
||||
(void)items; (void)count; (void)display_width; (void)dense_menu;
|
||||
(void)in_place;
|
||||
}
|
||||
int32_t ws_ui_anchor_for_bounds(int32_t x, int32_t width,
|
||||
int32_t display_width) {
|
||||
(void)width;
|
||||
if (x * 3 < display_width) return -1;
|
||||
if (x * 3 > display_width * 2) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpu_vram_dirty_mark_all(void) {}
|
||||
void gpu_vram_dirty_mark_row_impl(uint32_t y) { (void)y; }
|
||||
void psx_irq_raise(uint32_t bit, uint32_t detail) { (void)bit; (void)detail; }
|
||||
void event_ring_record_aux(uint16_t kind, uint8_t detail, uint32_t aux) {
|
||||
(void)kind; (void)detail; (void)aux;
|
||||
}
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
int psx_netplay_active(void) { return 0; }
|
||||
int sio_hold_present_for_card(void) { return 0; }
|
||||
uint32_t psx_compiled_irq_resume_pc(void) { return 0; }
|
||||
uint32_t psx_last_irq_check_pc(void) { return 0; }
|
||||
uint32_t psx_netplay_rb_sticky_bb_pc(void) { return 0; }
|
||||
void mod_runtime_on_vblank(void) {}
|
||||
bool screen_kind_from_name(const char *name, ScreenKind *out) {
|
||||
(void)name;
|
||||
if (out) *out = SCREEN_RAW;
|
||||
return true;
|
||||
}
|
||||
ColorLut *color_lut_create(const ColorSettings *settings) {
|
||||
(void)settings;
|
||||
return NULL;
|
||||
}
|
||||
void color_lut_destroy(ColorLut *lut) { (void)lut; }
|
||||
bool color_lut_is_passthrough(const ColorLut *lut) {
|
||||
(void)lut;
|
||||
return true;
|
||||
}
|
||||
void color_lut_map555(const ColorLut *lut, uint16_t bgr555,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b) {
|
||||
(void)lut;
|
||||
if (r) *r = (uint8_t)((bgr555 & 0x1Fu) << 3);
|
||||
if (g) *g = (uint8_t)(((bgr555 >> 5) & 0x1Fu) << 3);
|
||||
if (b) *b = (uint8_t)(((bgr555 >> 10) & 0x1Fu) << 3);
|
||||
}
|
||||
uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) { return address; }
|
||||
void text_xlate_vram_upload(int x, int y, int w, int h) {
|
||||
(void)x; (void)y; (void)w; (void)h;
|
||||
}
|
||||
uint32_t debug_guest_ra(void) { return 0; }
|
||||
uint32_t debug_guest_sp(void) { return 0; }
|
||||
uint8_t *memory_get_ram_ptr(void) { return (uint8_t *)test_ram; }
|
||||
|
||||
void gr_init(uint16_t *vram_ptr) { (void)vram_ptr; }
|
||||
void gr_set_scale(int scale) { (void)scale; }
|
||||
int gr_scale(void) { return 1; }
|
||||
void gr_set_texture_filter(int bilinear) { (void)bilinear; }
|
||||
int gr_texture_filter(void) { return 0; }
|
||||
void gr_set_semi_transparency(int enabled, int mode) { (void)enabled; (void)mode; }
|
||||
void gr_set_mask_bits(int set_bit, int check_bit) { (void)set_bit; (void)check_bit; }
|
||||
void gr_set_texture_window(uint32_t raw) { (void)raw; }
|
||||
void gr_set_color_modulation(int r, int g, int b, int raw_texture) {
|
||||
(void)r; (void)g; (void)b; (void)raw_texture;
|
||||
}
|
||||
void gr_set_precise_triangle(int enabled, int32_t x0, int32_t y0,
|
||||
int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
|
||||
(void)enabled; (void)x0; (void)y0; (void)x1; (void)y1; (void)x2; (void)y2;
|
||||
}
|
||||
void gr_set_perspective_triangle(int enabled, float q0, float q1, float q2) {
|
||||
(void)enabled; (void)q0; (void)q1; (void)q2;
|
||||
}
|
||||
void gr_fill_rect(int x, int y, int w, int h, uint16_t color) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)color;
|
||||
}
|
||||
void gr_copy_rect(int src_x, int src_y, int dst_x, int dst_y, int w, int h) {
|
||||
(void)src_x; (void)src_y; (void)dst_x; (void)dst_y; (void)w; (void)h;
|
||||
}
|
||||
void gr_draw_flat_triangle(int x0, int y0, int x1, int y1, int x2, int y2,
|
||||
uint16_t color) {
|
||||
(void)x0; (void)y0; (void)x1; (void)y1; (void)x2; (void)y2; (void)color;
|
||||
}
|
||||
void gr_draw_gouraud_triangle(int x0, int y0, uint16_t c0,
|
||||
int x1, int y1, uint16_t c1,
|
||||
int x2, int y2, uint16_t c2) {
|
||||
(void)x0; (void)y0; (void)c0; (void)x1; (void)y1; (void)c1;
|
||||
(void)x2; (void)y2; (void)c2;
|
||||
}
|
||||
void gr_draw_textured_triangle(int x0, int y0, int u0, int v0,
|
||||
int x1, int y1, int u1, int v1,
|
||||
int x2, int y2, int u2, int v2,
|
||||
uint16_t clut_x, uint16_t clut_y,
|
||||
uint16_t texpage) {
|
||||
(void)x0; (void)y0; (void)u0; (void)v0; (void)x1; (void)y1; (void)u1;
|
||||
(void)v1; (void)x2; (void)y2; (void)u2; (void)v2; (void)clut_x;
|
||||
(void)clut_y; (void)texpage;
|
||||
}
|
||||
void gr_draw_shaded_textured_triangle(int x0, int y0, int u0, int v0,
|
||||
uint32_t color0, int x1, int y1,
|
||||
int u1, int v1, uint32_t color1,
|
||||
int x2, int y2, int u2, int v2,
|
||||
uint32_t color2, uint16_t clut_x,
|
||||
uint16_t clut_y, uint16_t texpage,
|
||||
int raw_texture) {
|
||||
(void)x0; (void)y0; (void)u0; (void)v0; (void)color0; (void)x1;
|
||||
(void)y1; (void)u1; (void)v1; (void)color1; (void)x2; (void)y2;
|
||||
(void)u2; (void)v2; (void)color2; (void)clut_x; (void)clut_y;
|
||||
(void)texpage; (void)raw_texture;
|
||||
}
|
||||
void gr_draw_flat_rect(int x, int y, int w, int h, uint16_t color) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)color;
|
||||
}
|
||||
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) {
|
||||
last_textured_rect.calls++;
|
||||
last_textured_rect.x = x;
|
||||
last_textured_rect.y = y;
|
||||
last_textured_rect.w = w;
|
||||
last_textured_rect.h = h;
|
||||
last_textured_rect.u = u;
|
||||
last_textured_rect.v = v;
|
||||
last_textured_rect.clut_x = clut_x;
|
||||
last_textured_rect.clut_y = clut_y;
|
||||
last_textured_rect.texpage = texpage;
|
||||
}
|
||||
void gr_draw_textured_rect_scaled(int x, int y, int w, int h,
|
||||
int u0, int v0, int u1, int v1,
|
||||
uint16_t clut_x, uint16_t clut_y,
|
||||
uint16_t texpage) {
|
||||
last_scaled_rect.calls++;
|
||||
last_scaled_rect.x=x; last_scaled_rect.y=y;
|
||||
last_scaled_rect.w=w; last_scaled_rect.h=h;
|
||||
last_scaled_rect.u0=u0; last_scaled_rect.v0=v0;
|
||||
last_scaled_rect.u1=u1; last_scaled_rect.v1=v1;
|
||||
(void)v1; (void)clut_x; (void)clut_y; (void)texpage;
|
||||
}
|
||||
void gr_draw_line(int x0, int y0, int x1, int y1, uint16_t color) {
|
||||
(void)x0; (void)y0; (void)x1; (void)y1; (void)color;
|
||||
}
|
||||
void gr_draw_shaded_line(int x0, int y0, uint16_t c0,
|
||||
int x1, int y1, uint16_t c1) {
|
||||
(void)x0; (void)y0; (void)c0; (void)x1; (void)y1; (void)c1;
|
||||
}
|
||||
int gr_render_display(uint32_t *out, int pitch, int dx, int dy, int dw, int dh) {
|
||||
(void)out; (void)pitch; (void)dx; (void)dy; (void)dw; (void)dh;
|
||||
return 0;
|
||||
}
|
||||
int gr_render_display_hires(uint32_t *out, int pitch, int dx, int dy, int dw, int dh) {
|
||||
(void)out; (void)pitch; (void)dx; (void)dy; (void)dw; (void)dh;
|
||||
return 0;
|
||||
}
|
||||
void gr_vram_write(int x, int y, uint16_t pixel) { (void)x; (void)y; (void)pixel; }
|
||||
uint16_t gr_vram_read(int x, int y) { (void)x; (void)y; return 0; }
|
||||
void gr_vram_transfer_in(int x, int y, int w, int h, const uint16_t *data) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)data;
|
||||
}
|
||||
void gr_vram_transfer_out(int x, int y, int w, int h, uint16_t *data) {
|
||||
(void)x; (void)y; (void)w; (void)h; (void)data;
|
||||
}
|
||||
void gr_set_draw_area(int x1, int y1, int x2, int y2) {
|
||||
(void)x1; (void)y1; (void)x2; (void)y2;
|
||||
}
|
||||
void gr_get_draw_area(int *x1, int *y1, int *x2, int *y2) {
|
||||
if (x1) *x1 = (int)draw_area_left;
|
||||
if (y1) *y1 = (int)draw_area_top;
|
||||
if (x2) *x2 = (int)draw_area_right;
|
||||
if (y2) *y2 = (int)draw_area_bottom;
|
||||
}
|
||||
void gr_set_draw_offset(int x, int y) { (void)x; (void)y; }
|
||||
int gr_wide_supported(void) { return 1; }
|
||||
void gr_wide_configure(int wide_w, int offset) { (void)wide_w; (void)offset; }
|
||||
void gr_wide_set_view(int enabled, int shift, int left, int right) {
|
||||
(void)enabled; (void)shift; (void)left; (void)right;
|
||||
}
|
||||
void gr_wide_set_target(int base_x) { (void)base_x; }
|
||||
void gr_wide_disable_target(void) {}
|
||||
void gr_wide_clear(int base_x, int y, int h, uint16_t color) {
|
||||
(void)base_x; (void)y; (void)h; (void)color;
|
||||
}
|
||||
void gr_wide_clear_margins(int base_x, int y, int h, uint16_t color, int sides) {
|
||||
(void)base_x; (void)y; (void)h; (void)color; (void)sides;
|
||||
}
|
||||
int gr_wide_dump_full(uint32_t *out, int cap_pixels, int *ow, int *oh, int base_x) {
|
||||
(void)out; (void)cap_pixels; (void)ow; (void)oh; (void)base_x;
|
||||
return 0;
|
||||
}
|
||||
int gr_render_wide_display(uint32_t *out, int pitch, int base_x,
|
||||
int disp_y, int disp_h) {
|
||||
(void)out; (void)pitch; (void)base_x; (void)disp_y; (void)disp_h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#ifdef NDEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
|
||||
/* auto_ui_squash must correct untextured HUD fills together with the textured
|
||||
* frame around them. Spider-Man's health bar is a gouraud quad (GP0 0x38) and
|
||||
* its webbing meter a flat quad (0x28) inside textured frames (0x2C); while
|
||||
* only textured quads were admitted, the fills kept their raw 4:3 X and
|
||||
* overhung the squashed frames at 21:9 and 32:9.
|
||||
*
|
||||
* Drives the real path: an ordering-table linked list in guest RAM, the UI
|
||||
* prepass, run grouping (real ws_ui_group.c), then GP0 execution. */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define GPU_EXEC_REAL_UI_GROUP
|
||||
#include "../src/gpu.c"
|
||||
|
||||
#include "gpu_exec_stubs.inc"
|
||||
|
||||
#define OT_HEAD 0x00010000u
|
||||
#define NODE_RING 0x00010100u
|
||||
#define NODE_FILL 0x00010200u
|
||||
#define NODE_FLAT 0x00010300u
|
||||
|
||||
static uint32_t pack_vertex(int16_t x, int16_t y) {
|
||||
return (uint16_t)x | ((uint32_t)(uint16_t)y << 16);
|
||||
}
|
||||
|
||||
static void put_node(uint32_t addr, uint32_t next, const uint32_t *words,
|
||||
uint32_t count) {
|
||||
test_ram[addr / 4u] = (count << 24) | (next & 0xFFFFFFu);
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
test_ram[addr / 4u + 1u + i] = words[i];
|
||||
}
|
||||
|
||||
/* Frame: textured quad over [x0, x1). Fills inside it, both untextured. */
|
||||
static void build_hud(int16_t x0, int16_t x1, int16_t fx0, int16_t fx1) {
|
||||
memset(test_ram, 0, sizeof(test_ram));
|
||||
const uint32_t frame[9] = {
|
||||
0x2C808080u, pack_vertex(x0, 20), 0x00000000u,
|
||||
pack_vertex(x1, 20), 0x00080000u,
|
||||
pack_vertex(x0, 36), 0x00001000u,
|
||||
pack_vertex(x1, 36), 0x00001010u,
|
||||
};
|
||||
const uint32_t gouraud[8] = {
|
||||
0x3800FF00u, pack_vertex(fx0, 26),
|
||||
0x0000FF80u, pack_vertex(fx1, 26),
|
||||
0x0000FF00u, pack_vertex(fx0, 30),
|
||||
0x0000FF80u, pack_vertex(fx1, 30),
|
||||
};
|
||||
const uint32_t flat[5] = {
|
||||
0x28802080u, pack_vertex(fx0, 31), pack_vertex(fx1, 31),
|
||||
pack_vertex(fx0, 34), pack_vertex(fx1, 34),
|
||||
};
|
||||
test_ram[OT_HEAD / 4u] = NODE_RING; /* empty OT entry: rank 0 */
|
||||
put_node(NODE_RING, NODE_FILL, frame, 9);
|
||||
put_node(NODE_FILL, NODE_FLAT, gouraud, 8);
|
||||
put_node(NODE_FLAT, 0xFFFFFFu, flat, 5);
|
||||
}
|
||||
|
||||
static void load_packet(uint32_t node, uint32_t count) {
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
gp0_cmd_buf[i] = test_ram[node / 4u + 1u + i];
|
||||
gp0_cmd_source_addr = node + 4u;
|
||||
gp0_words_needed = (int)count;
|
||||
}
|
||||
|
||||
static void reset_state(int in_place) {
|
||||
s_frame_count = 100;
|
||||
draw_offset_x = draw_offset_y = 0;
|
||||
draw_area_left = 0; draw_area_top = 0;
|
||||
draw_area_right = 1023; draw_area_bottom = 511;
|
||||
hres1 = 1; hres2 = 0; video_mode = 0; display_depth = 0;
|
||||
display_disabled = 0; display_area_x = 0; display_area_y = 0;
|
||||
h_display_x1 = 0x200; h_display_x2 = 0xC00;
|
||||
v_display_y1 = 0x010; v_display_y2 = 0x100;
|
||||
/* Projection-and-stretch path at 32:9: X squash 3/8, not native-wide. */
|
||||
ws_mode = 0;
|
||||
ws_cfg_num = 32; ws_cfg_den = 9;
|
||||
ws_xnum = 3; ws_xden = 8;
|
||||
ws_full_2d = 0;
|
||||
gpu_ws_set_auto_ui_squash(1);
|
||||
gpu_ws_set_auto_ui_in_place(in_place);
|
||||
}
|
||||
|
||||
/* Prepass the list and execute the two fills; returns their drawn X span. */
|
||||
static void run_fills(int *gouraud_min, int *gouraud_max,
|
||||
int *flat_min, int *flat_max) {
|
||||
gpu_ws_prepass_linked_list(OT_HEAD);
|
||||
assert(ws_ui_prepass_count == 3); /* frame + both untextured fills */
|
||||
|
||||
gpu_exec_reset_triangles();
|
||||
load_packet(NODE_FILL, 8);
|
||||
gp0_exec_shaded_quad();
|
||||
assert(gpu_exec_triangles.calls == 2);
|
||||
*gouraud_min = gpu_exec_triangles.min_x;
|
||||
*gouraud_max = gpu_exec_triangles.max_x;
|
||||
|
||||
gpu_exec_reset_triangles();
|
||||
load_packet(NODE_FLAT, 5);
|
||||
gp0_exec_mono_quad();
|
||||
assert(gpu_exec_triangles.calls >= 1);
|
||||
*flat_min = gpu_exec_triangles.min_x;
|
||||
*flat_max = gpu_exec_triangles.max_x;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int gmin, gmax, fmin, fmax;
|
||||
|
||||
/* in_place: the fill and its frame share the run's own centre. */
|
||||
reset_state(1);
|
||||
build_hud(60, 128, 64, 120);
|
||||
run_fills(&gmin, &gmax, &fmin, &fmax);
|
||||
const int32_t centre = 60 + (128 - 60) / 2;
|
||||
assert(gmin == ws_scale_about(64, centre));
|
||||
assert(gmax == ws_scale_about(120, centre));
|
||||
assert(fmin == gmin && fmax == gmax);
|
||||
/* Inside the squashed frame, not overhanging it. */
|
||||
assert(gmin >= ws_scale_about(60, centre));
|
||||
assert(gmax <= ws_scale_about(128, centre));
|
||||
|
||||
/* edges (default): a left-third run pins to the display's left edge. */
|
||||
reset_state(0);
|
||||
build_hud(60, 128, 64, 120);
|
||||
run_fills(&gmin, &gmax, &fmin, &fmax);
|
||||
const int32_t left = ws_disp_x();
|
||||
assert(gmin == ws_scale_about(64, left));
|
||||
assert(gmax == ws_scale_about(120, left));
|
||||
assert(fmin == gmin && fmax == gmax);
|
||||
|
||||
/* A non-axis-aligned untextured quad is world geometry, never UI. */
|
||||
reset_state(1);
|
||||
build_hud(60, 128, 64, 120);
|
||||
test_ram[NODE_FILL / 4u + 1u + 3u] = pack_vertex(121, 25); /* skew v1 */
|
||||
gpu_ws_prepass_linked_list(OT_HEAD);
|
||||
assert(ws_ui_prepass_count == 2);
|
||||
|
||||
puts("ws_auto_ui_untextured_exec_test: PASS");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user