Files
psxrecomp/runtime/tests/gpu_exec_stubs.inc
2026-10-24 15:24:21 -07:00

481 lines
20 KiB
C++

/* 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;
uint64_t psx_cycle_count; /* gpu_timeline.c stamps notes with it */
int g_exec_phase;
int g_psx_render_pass_active;
void psx_mod_counter_add(const char *name, uint32_t delta) { (void)name; (void)delta; }
static int fixture_pgxp_enabled;
int pgxp_enabled(void) { return fixture_pgxp_enabled; }
static int fixture_pgxp_cpu_mode;
int pgxp_cpu_mode(void) { return fixture_pgxp_cpu_mode; }
void pgxp_set_cpu_mode(int enabled) { fixture_pgxp_cpu_mode=enabled; }
int pgxp_projective_scope_begin(PGXPProjectiveScope *scope) {
if (!scope || scope->active) return 0;
scope->saved_enabled=fixture_pgxp_enabled;
scope->saved_cpu_mode=fixture_pgxp_cpu_mode;
fixture_pgxp_enabled=1;
fixture_pgxp_cpu_mode=1;
scope->active=1;
return 1;
}
void pgxp_projective_scope_end(PGXPProjectiveScope *scope) {
if (!scope || !scope->active) return;
fixture_pgxp_cpu_mode=scope->saved_cpu_mode;
fixture_pgxp_enabled=scope->saved_enabled;
scope->active=0;
}
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. */
static GrBackend fixture_backend = GR_BACKEND_SOFTWARE;
GrBackend gr_backend(void) { return fixture_backend; }
int gl_renderer_texture_banks_supported(void) { return 0; }
static int fixture_projective_supported, fixture_projective_draws;
static PSXProjectedVertex fixture_projective_vertices[3];
int gl_renderer_projective_supported(void) { return fixture_projective_supported; }
void gl_renderer_draw_projected_triangle(const PSXProjectedVertex v[3],
uint16_t tp, uint16_t cx, uint16_t cy, int raw, int semi, int perspective) {
memcpy(fixture_projective_vertices,v,sizeof fixture_projective_vertices);
fixture_projective_draws++;
(void)tp;(void)cx;(void)cy;(void)raw;(void)semi;(void)perspective;
}
static struct { uint32_t addr,word; PGXPProjection p; int valid; } fixture_homogeneous[4];
int pgxp_load_projection(uint32_t addr, uint32_t word, PGXPProjection *out) {
for(int i=0;i<4;++i) if(fixture_homogeneous[i].valid &&
fixture_homogeneous[i].addr==addr && fixture_homogeneous[i].word==word) {
if(out)*out=fixture_homogeneous[i].p;
return 1;
}
return 0;
}
float pgxp_get_unclamped_depth(uint32_t addr, uint32_t word) {
PGXPProjection projection;
return pgxp_load_projection(addr, word, &projection) ? projection.z : 0.0f;
}
static int fixture_projection_tracking;
int pgxp_projection_tracking(void) { return fixture_projection_tracking; }
void pgxp_set_projection_tracking(int enabled) { fixture_projection_tracking=!!enabled; }
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];
/* The enhancement GPU-DMA aperture (mod_memory.h): a test allocates it by
* setting test_aperture_used; reads and keys then follow the real rules. */
static uint32_t test_aperture[0x00020000u / 4u];
static uint32_t test_aperture_used;
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;
/* PGXP stub state: the quad case in main() switches geometry correction on
* and makes the packet words in a bitmask (bit n = word n at g_test_quad_base)
* report a dataflow-precise vertex. */
static int g_test_geometry_correction = 0;
static uint32_t g_test_quad_base = 0xFFFFFFFFu;
static uint32_t g_test_precise_words = 0;
static int g_test_rect_bypass = 0;
static int g_test_rect_partial = 0;
static int test_word_precise(uint32_t addr) {
if (g_test_quad_base == 0xFFFFFFFFu || addr < g_test_quad_base) return 0;
uint32_t word = (addr - g_test_quad_base) / 4u;
return word < 32u && ((g_test_precise_words >> word) & 1u);
}
static int g_test_precise_triangles = 0;
static int g_test_textured_triangles = 0;
/* Bounding box of every flat/gouraud triangle drawn since the last reset. */
static struct { int calls, min_x, max_x, min_y, max_y; } gpu_exec_triangles;
static inline void gpu_exec_reset_triangles(void) {
gpu_exec_triangles.calls = 0;
gpu_exec_triangles.min_x = gpu_exec_triangles.min_y = 1 << 30;
gpu_exec_triangles.max_x = gpu_exec_triangles.max_y = -(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}, ys[3] = {y0, y1, 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];
if (ys[i] < gpu_exec_triangles.min_y) gpu_exec_triangles.min_y = ys[i];
if (ys[i] > gpu_exec_triangles.max_y) gpu_exec_triangles.max_y = ys[i];
}
}
uint32_t psx_read_word(uint32_t addr) {
uint32_t phys = addr & 0x1FFFFFFFu, off;
if (psx_mod_gpu_dma_aperture_offset_for(phys, 4u, test_aperture_used, &off))
return test_aperture[off / 4u];
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) { fixture_pgxp_enabled = enabled; }
int gte_geometry_correction_enabled(void) { return g_test_geometry_correction; }
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)packet_word;
*x16 = int_x * 65536;
*y16 = int_y * 65536;
*sz = 0;
if (test_word_precise(addr)) {
*x16 += 0x4000;
return PGXP_SRC_DATAFLOW;
}
return PGXP_SRC_NATIVE;
}
static struct {
uint32_t addr, word;
int32_t x16, y16;
uint16_t z;
int valid;
} fixture_projections[4];
int pgxp_probe_precise_vertex(uint32_t addr, uint32_t packet_word,
int32_t int_x, int32_t int_y) {
(void)packet_word; (void)int_x; (void)int_y;
return test_word_precise(addr) ? PGXP_SRC_DATAFLOW : PGXP_SRC_NATIVE;
}
void pgxp_note_triangle(int precise) { (void)precise; }
void pgxp_note_triangle_detail(const PGXPTriRecord *rec) { (void)rec; }
void pgxp_note_rect_bypass(int all_precise) {
if (all_precise) g_test_rect_bypass++;
else g_test_rect_partial++;
}
int gte_precision_load_word(uint32_t addr, uint32_t packed,
int32_t *x16, int32_t *y16, uint16_t *z) {
for (unsigned i = 0; i < 4; ++i) {
if (fixture_projections[i].valid && fixture_projections[i].addr == addr &&
fixture_projections[i].word == packed) {
if (x16) *x16 = fixture_projections[i].x16;
if (y16) *y16 = fixture_projections[i].y16;
if (z) *z = fixture_projections[i].z;
return fixture_projections[i].z != 0;
}
}
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 psx_mod_gpu_dma_resolve_address_for(address, test_aperture_used);
}
int psx_mod_gpu_dma_memory_contains(uint32_t address, uint32_t bytes) {
return bytes && psx_mod_gpu_dma_aperture_offset_for(
address, bytes, test_aperture_used, NULL);
}
uint32_t psx_gpu_packet_key(uint32_t address) {
return psx_gpu_packet_key_for(address, test_aperture_used);
}
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_dither(int enabled) { (void)enabled; }
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;
}
static struct { int enabled; int32_t xy[6]; } fixture_precise_triangle;
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) {
fixture_precise_triangle.enabled = enabled;
int32_t xy[6] = { x0, y0, x1, y1, x2, y2 };
memcpy(fixture_precise_triangle.xy, xy, sizeof xy);
if (enabled) g_test_precise_triangles++;
}
void gr_set_perspective_triangle(int enabled, float q0, float q1, float q2) {
(void)enabled; (void)q0; (void)q1; (void)q2;
}
void gr_set_depth_triangle(int enabled, float z0, float z1, float z2) {
(void)enabled; (void)z0; (void)z1; (void)z2;
}
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) {
g_test_textured_triangles++;
(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) {
#ifdef GPU_EXEC_RECORD_SHADED_TEXTURED
gpu_exec_record_triangle(x0, y0, x1, y1, x2, y2);
#endif
(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_upload_begin(int x,int y,int w,int h) { (void)x; (void)y; (void)w; (void)h; }
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_upload_commit(int x,int y,int w,int h,const uint16_t *d) { gr_vram_transfer_in(x,y,w,h,d); }
void gr_vram_upload_set_open(int open) { (void)open; }
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; }
static struct { int enabled, shift, left, right; } fixture_wide_view;
void gr_wide_set_view(int enabled, int shift, int left, int right) {
fixture_wide_view.enabled = enabled;
fixture_wide_view.shift = shift;
fixture_wide_view.left = left;
fixture_wide_view.right = 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;
}
void gr_wide_feedback_rect(int src_base, int src_x, int src_y, int dst_x, int dst_y,
int w, int h, int r, int g, int b, int raw, int semi) {
(void)src_base; (void)src_x; (void)src_y; (void)dst_x; (void)dst_y;
(void)w; (void)h; (void)r; (void)g; (void)b; (void)raw; (void)semi;
}
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;
}
int gte_nclip_native_wide_sign(int32_t mac0, int* sign) {
(void)mac0; (void)sign; return 0;
}
int gte_nclip_native_wide_previous_sign(int32_t mac0, int* sign) {
(void)mac0; (void)sign; return 0;
}
int gte_nclip_zero_positive(int32_t mac0, uint32_t pc) {
(void)mac0; (void)pc; return 0;
}
static int fixture_triangle_recovered;
void gl_renderer_note_wide_triangle_recovery(int enabled) {
fixture_triangle_recovered=enabled;
}
/* Smooth motion (frame generation) vertex sources: off in these fixtures. */
int gl_renderer_frame_generation(void) { return 0; }
/* PGXP renderer features: off in these fixtures. */
int gl_renderer_pgxp_render_wanted(void) { return 0; }
void gl_renderer_fg_source(const uint32_t id[3], const int32_t pc[9], const int32_t h[3],
const int32_t x[3], const int32_t y[3]) {
(void)id; (void)pc; (void)h; (void)x; (void)y;
}
void gte_fg_source_set(int enabled) { (void)enabled; }
int gte_fg_source_enabled(void) { return 0; }
int gte_fg_source_lookup(uint32_t packed, GteFgSrc out[2]) { (void)packed; (void)out; return 0; }