Files
psxrecomp/runtime/include/gpu_gl_renderer.h
T
Matthew Stanley 0631538407 gl-renderer v2: GPU-authoritative VRAM FBO + Beetle-style filtered sampling
Full rewrite of the GL backend around a single authoritative hr VRAM FBO
(RGBA8 + stencil mask-bit mirror, internal scale from [video]
supersampling): GPU fills/copies/draws rasterize into it, CPU uploads
batch through a pending-rect quad blit, a PACK pass re-encodes dirty
regions into the native R16UI raw mirror for texture sampling and
readback, and gpu.c routes GPUREAD/mask-check/peek reads through the
facade so the CPU array syncs before any CPU-side read.

Texture sampling adopts the Beetle-PSX model (structure only, studied
from beetle-psx-libretro):
- Per-primitive uv sampling bounds, clamped in the fragment shader when
  no texture window is active. Filtered neighbours and S>1 interpolation
  overshoot can never read outside the prim's own texture rect. Bounds
  back off the exclusive max edge for axis-aligned (2D) uv mappings and
  widen to the full page when the uv range crosses a 256 wrap boundary.
- Bilinear takes the NEAREST texel as base (cutout + STP authority),
  neighbours toward the sub-texel offset, with per-texel opacity weights
  and renormalised colour. The old "-0.5 then floor" base sampled one
  texel OUTSIDE the prim on top/left edges; where that neighbour was
  empty VRAM (e.g. tiled menu backgrounds with u0=0) the whole edge
  column/row discarded — the vertical line on Tomba's title and the
  cross seams on the memory-card screens.

The software renderer's bilinear gets the same reformulation so GL and
SW render filtered output identically; the SW nearest path (shipping
default) is untouched.

Also: always-on coherency event ring (flushes, fills, copies, draw
bboxes, packs, readbacks, presents, probe perturbations, with rects +
frame numbers) queryable via the debug server, and gl_vram_diff now
forces a full pack so it reads FBO truth even where the raw-mirror
invariant is broken.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 05:19:01 -07:00

80 lines
3.3 KiB
C

#ifndef PSX_GPU_GL_RENDERER_H
#define PSX_GPU_GL_RENDERER_H
/* GL backend context + present entry points, called from main.cpp's window
* setup and present path when [video] renderer = "opengl". The backend's
* rasterization vtable is obtained separately via gl_backend_get()
* (gpu_render.h). SDL_Window is forward-declared (SDL typedefs it from this
* same struct tag) so this header needs no SDL include. */
#include <stdint.h>
struct SDL_Window;
#ifdef __cplusplus
extern "C" {
#endif
/* Create the GL context on a window made with SDL_WINDOW_OPENGL.
* Returns 1 on success, 0 to fall back to the SDL_Renderer present path. */
int gl_renderer_init_context(struct SDL_Window *win);
/* Present an ARGB8888 image (BGRA byte order) as a letterboxed quad + swap.
* Used for 24-bit (FMV) frames and the PSX_GL_FORCE_CPU_PRESENT diagnostic. */
void gl_renderer_present(const uint32_t *pixels, int src_w, int src_h, int linear);
/* Clear to black + swap (display-disabled frame). */
void gl_renderer_present_blank(void);
/* Sync the authoritative FBO down to CPU VRAM if the GPU side is ahead (else
* a no-op). The 24-bit (FMV) present path, screenshots, and the debug server
* call this before reading CPU VRAM. */
void gl_renderer_sync_cpu(void);
/* THE present path for 15-bit frames: blit the display region straight from
* the authoritative VRAM FBO into a 4:3 letterboxed rect (no readback).
* Deterministic — used for every 15-bit frame. linear = filter on scale. */
void gl_renderer_present_vram(int disp_x, int disp_y, int w, int h, int linear);
void gl_renderer_shutdown(void);
/* Diagnostics (debug server): read GPU-side VRAM without touching the CPU
* array; report coherency flags + dirty rects. fbo_peek returns 0 when the
* GL pipeline is inactive. */
int gl_renderer_fbo_peek(int x, int y, int w, int h, uint16_t *out);
void gl_renderer_diag(int *gpu_dirty, int pending[5], int pack[5]);
/* Always-on coherency event ring (debug server "gl_coh_ring"): every upload
* flush, fill, copy, draw bbox, pack, full readback, present, and probe
* perturbation, with rect + frame. An op that flushes internally records its
* own event AFTER the FLUSH it caused (the event after a FLUSH = trigger). */
enum {
GL_COH_FLUSH = 1, /* CPU->FBO upload flush (pending box) */
GL_COH_FILL = 2, /* GP0(02) fill rect */
GL_COH_COPY_SRC = 3, /* GP0(80) copy, source rect */
GL_COH_COPY = 4, /* GP0(80) copy, dest rect */
GL_COH_DRAW = 5, /* drawn prim bbox (clipped to draw area) */
GL_COH_PACK = 6, /* hr FBO -> raw mirror pack (dirty box) */
GL_COH_ENSURE = 7, /* full FBO -> CPU VRAM readback */
GL_COH_PRESENT = 8, /* 15-bit present blit (display rect) */
GL_COH_UPLOAD = 9, /* bulk CPU->VRAM transfer_in dest rect */
GL_COH_PEEK = 10, /* gl_fbo_peek probe (perturbs: flushes) */
GL_COH_DIFF = 11, /* gl_vram_diff probe (perturbs: flushes) */
};
typedef struct {
uint32_t frame;
uint8_t kind;
int16_t x0, y0, x1, y1; /* native VRAM coords, inclusive */
} GlCohEvent;
uint64_t gl_renderer_coh_total(void);
/* Fetch event by absolute sequence number; 0 if evicted or out of range. */
int gl_renderer_coh_get(uint64_t seq, GlCohEvent *out);
#ifdef __cplusplus
}
#endif
#endif /* PSX_GPU_GL_RENDERER_H */