mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-27 02:32:02 -04:00
8490d8b4cb
Render the genuine 16:9 FOV at correct proportions instead of squash-then-stretch, using a separate per-framebuffer wide surface so canonical PSX VRAM stays faithful (4:3 / non-opted games byte-identical; the wide canvas only exists when opted in). - Wide-aspect mode descriptor (off / squash / native-wide) in gpu.c; opt-in via [video] aspect_ratio. GTE fed the 4:3 ratio (identity, no squash) in native-wide; canonical draw_offset/draw_area left faithful (no VRAM widening, no wrap). - Separate wide present-surfaces (gpu_sw_renderer.c) keyed by back-buffer base: framebuffer prims mirrored at local x = vram_x - base_x + OFFSET; margins cleared via the frame-clear mirror; present reads the displayed buffer's surface 1:1. Eliminates the A/B bleed + flicker (and the segfault) of the shared-canvas attempt. gr_* vtable extended; GL backend reports unsupported -> canonical present until the GL port (task 8). - psx_ws_x_margin native-wide-aware (reuses the existing [widescreen.cull] emit). CONFIRMED a no-op for Tomba's edge objects: they spawn via a non-FUN_80022E44 path (the foreground classifier is already 16:9-safe). See handoff. - Draw-census extended with per-prim screen-X extent + back-buffer base for the native-wide void diagnosis; ws_nw TCP toggle; gpu_state ws.mode/nw_extra. - Controller: "auto"/"gamepad"/"controller" device = first available SDL pad. WIP checkpoint. SW compositor works: flicker fixed, faithful, terrain platform fills the wide frame. REMAINING: discrete foreground OBJECTS (rock pillar, tree branch, bushes) + the parallax backdrop are still culled at the 4:3 edge via a separate generation/spawn path (wtrace the OT display-list writers next); GL compositor port. Detail in NATIVE_WIDE_PLAN.md + the session handoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
161 lines
7.3 KiB
C
161 lines
7.3 KiB
C
/* gpu.h — PS1 GPU hardware simulation (Phase 3).
|
|
*
|
|
* Implements GPUSTAT, GP0, GP1, and 1024x512 16-bit VRAM.
|
|
* No rendering to screen — just correct hardware state transitions.
|
|
*/
|
|
|
|
#ifndef PSXRECOMP_GPU_H
|
|
#define PSXRECOMP_GPU_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
void gpu_init(void);
|
|
uint32_t gpu_read_gpustat(void); /* 0x1F801814 read */
|
|
uint32_t gpu_read_gpuread(void); /* 0x1F801810 read */
|
|
void gpu_write_gp0(uint32_t val); /* 0x1F801810 write */
|
|
void gpu_write_gp1(uint32_t val); /* 0x1F801814 write */
|
|
void gpu_set_gp0_source(uint32_t addr); /* diagnostic source for next GP0 word */
|
|
void gpu_vblank_tick(void); /* Toggle LCF, called at each simulated vblank */
|
|
|
|
/* Display presentation accessors (Phase 3). */
|
|
const uint16_t* gpu_get_vram(void); /* Pointer to 1024x512 16-bit VRAM */
|
|
|
|
typedef struct {
|
|
uint32_t display_x, display_y; /* VRAM start of display area (GP1(05h)) */
|
|
uint32_t width, height; /* Derived from display mode + ranges */
|
|
int depth24; /* GP1(08h) display depth flag: RGB888 scanout */
|
|
int disabled; /* GP1(03h) display disable flag */
|
|
} GpuDisplayInfo;
|
|
|
|
void gpu_get_display_info(GpuDisplayInfo* out);
|
|
void gpu_display_pixel_rgb(const GpuDisplayInfo* di, uint32_t x, uint32_t y,
|
|
uint8_t* r, uint8_t* g, uint8_t* b);
|
|
uint32_t gpu_display_pixel_argb(const GpuDisplayInfo* di, uint32_t x, uint32_t y);
|
|
uint64_t gpu_get_gp0_count(void); /* Total GP0 writes since init */
|
|
void gpu_get_gp0_stats(uint64_t* nop, uint64_t* fill, uint64_t* draw, uint64_t* env, uint64_t* copy);
|
|
|
|
typedef struct {
|
|
uint32_t left, top, right, bottom;
|
|
int32_t offset_x, offset_y;
|
|
} GpuDrawArea;
|
|
void gpu_get_draw_area(GpuDrawArea* out);
|
|
uint16_t gpu_vram_peek(int x, int y);
|
|
|
|
/* Shaded quad vertex capture (Phase 4.5 debug). */
|
|
typedef struct {
|
|
int32_t vx[4], vy[4];
|
|
uint32_t color[4];
|
|
} GpuSqCapEntry;
|
|
void gpu_arm_shaded_quad_capture(void);
|
|
int gpu_get_shaded_quad_capture(const GpuSqCapEntry** out);
|
|
|
|
/* Per-frame GP0 command ring (always-on; query via debug server).
|
|
* Each entry records the GP0 command header + up to 6 payload words
|
|
* (longer commands like 0x3C shaded textured quad are truncated to 6).
|
|
* Stamped with the s_frame_count value at issue time so a debug
|
|
* client can pull all commands for any frame in the ring window. */
|
|
#define GPU_GP0_RING_MAX_WORDS 12
|
|
typedef struct {
|
|
uint32_t frame;
|
|
uint32_t seq;
|
|
uint32_t src_addr; /* RAM/MMIO source address of command header, if known */
|
|
uint32_t pc; /* g_debug_last_store_pc when command completes */
|
|
uint8_t opcode;
|
|
uint8_t n_words; /* total command length; >MAX means truncated */
|
|
uint16_t pad;
|
|
uint32_t cmd[GPU_GP0_RING_MAX_WORDS];
|
|
} GpuGp0RingEntry;
|
|
|
|
uint64_t gpu_gp0_ring_total(void);
|
|
uint32_t gpu_gp0_ring_capacity(void);
|
|
uint32_t gpu_gp0_ring_max_words(void);
|
|
int gpu_gp0_ring_dump_frame(uint32_t frame, GpuGp0RingEntry *out, int max_out);
|
|
void gpu_gp0_ring_frame_span(uint32_t *out_oldest, uint32_t *out_newest);
|
|
|
|
/* Vblank presentation callback — called from gpu_vblank_tick(). */
|
|
typedef void (*gpu_vblank_cb)(void);
|
|
void gpu_set_vblank_callback(gpu_vblank_cb cb);
|
|
|
|
/* Present-time screen-colour model (see color_lut.h ScreenKind: 0=raw,
|
|
* 1=crt, 2=composite, 3=trinitron). Config/launcher-driven; the PSX_SCREEN
|
|
* env var, if set, overrides this. Default 0 (raw) is byte-identical. */
|
|
void gpu_set_screen_kind(int kind);
|
|
|
|
/* Widescreen proportion correction (active only when aspect != 4:3 and the
|
|
* game's [widescreen] block opts in — see config_loader.h). Tagged
|
|
* character/billboard prims are re-squashed around their projected anchor;
|
|
* untagged SPRT prims (screen-space HUD/menus) around the display centre.
|
|
* psx_ws_sprite_tag is the per-prim callback the recompiler emits at the
|
|
* entry of each [widescreen] sprite_tag_funcs function. */
|
|
/* mode: 0 = off (4:3 identity), 1 = squash (legacy hack), 2 = native-wide
|
|
* (render the wider FOV into a wider frame, present 1:1; GTE not squashed). */
|
|
void gpu_ws_configure(int aspect_num, int aspect_den,
|
|
uint32_t sprite_anchor_addr, int hud_sprt_squash, int mode);
|
|
struct CPUState;
|
|
void psx_ws_sprite_tag(struct CPUState* cpu);
|
|
|
|
/* Native-wide (mode 2) on a game frame. ws_nw_extra() is the total width the
|
|
* frame grows by, in display pixels (the present path widens the display read
|
|
* by this; 0 when native-wide is inactive). */
|
|
int ws_native_wide_active(void);
|
|
int ws_nw_extra(void);
|
|
/* True when the current frame must present at native 4:3 (FMV video or a
|
|
* full-2D menu/title screen), so the squash is suppressed and content drawn
|
|
* pixel-native. The present path uses the same predicate to pillarbox. */
|
|
int gpu_ws_present_native_43(void);
|
|
/* Per-side X cull-margin (screen/world units) emitted into the game's draw-
|
|
* cull immediates by the recompiler ([widescreen.cull]); 0 unless stretching. */
|
|
int psx_ws_x_margin(void);
|
|
|
|
/* Backdrop screen-X correction ([widescreen.backdrop] x_sites). The parallax
|
|
* 2D backdrop layer computes screen-X without the GTE, so it misses the
|
|
* widescreen squash; the recompiler emits this on each backdrop handler's
|
|
* final screenX store to squash it around the screen centre (identity at 4:3).
|
|
* Pulls far backdrop pieces in from past the 320px edge to cover the 16:9 FOV. */
|
|
int psx_ws_backdrop_x(int x);
|
|
|
|
/* Backdrop store-site registry: the runtime registers the [widescreen.backdrop]
|
|
* x_sites here (from game.toml) so the dirty-RAM interpreter applies the same
|
|
* psx_ws_backdrop_x() squash at those `sh` PCs that the recompiler emits into
|
|
* native cache DLLs — overlay code frequently runs interpreted, where the emit
|
|
* can't reach. PCs are masked to the physical (KUSEG) range. */
|
|
void psx_ws_set_backdrop_sites(const uint32_t* pcs, int n);
|
|
int psx_ws_is_backdrop_site(uint32_t pc);
|
|
|
|
/* Live widescreen state for diagnostics (TCP gpu_state). All pointers
|
|
* optional. last_tag_frame/cur_frame let the caller see game_mode freshness. */
|
|
typedef struct {
|
|
int configured; /* ws_xnum != ws_xden */
|
|
int active; /* squash currently applied this frame */
|
|
int game_mode; /* tagged char/billboard prim within 2 frames */
|
|
int present_native_43; /* frame presents pillarboxed 4:3 (FMV/full-2D) */
|
|
int x_margin; /* psx_ws_x_margin() right now */
|
|
int xnum, xden; /* squash factor */
|
|
int mode; /* 0 = off, 1 = squash, 2 = native-wide */
|
|
int nw_extra; /* native-wide frame growth (display px), 0 if off */
|
|
uint64_t cur_frame;
|
|
uint32_t last_tag_frame; /* frame of newest tagged prim */
|
|
} GpuWsDebug;
|
|
void gpu_ws_get_debug(GpuWsDebug* out);
|
|
|
|
/* Diagnostic: force psx_ws_x_margin() to return v (>=0) regardless of state,
|
|
* or -1 to restore the normal computed margin. For live cull-margin sweeps. */
|
|
void gpu_ws_set_margin_override(int v);
|
|
|
|
/* Always-on draw-census ring: every drawn primitive records frame / source
|
|
* addr / camera / first-vertex screen pos, so object spawn/despawn and edge
|
|
* culls are observable in data. Dump frames [f0,f1] to a CSV file. */
|
|
int gpu_ws_census_dump(uint32_t f0, uint32_t f1, const char *path);
|
|
void gpu_ws_census_set(int on);
|
|
uint64_t gpu_ws_census_seq(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PSXRECOMP_GPU_H */
|