mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
OpenGL backend: record on the emulation thread, replay on the render thread
With the render thread running, the backend's facade table records every state change, primitive, fill, copy, CPU->VRAM upload (payload copied) and native-wide call, and the present, into the command ring; the render thread replays them through the unchanged glb_* functions. With it off gl_backend_get returns the existing table and nothing new runs. Sync points: every other GL entry point reached on the emulation thread (readbacks, savestate restage, render passes, OpenXR, settings, diagnostics) drains the ring and takes the context (GL_RT_SYNC); the emulation thread then runs the rest of that frame directly, exactly as without the thread, and hands the context back at the frame boundary when the frame may run asynchronously again (no depth24 display, dual-raster, frame interpolation, render pass, netplay or OpenXR). Guest-visible VRAM stays gpu.c's array: while the render thread owns the context, uploads stage from a private copy that receives each payload in command order. Guest state the backend reads while drawing (per-primitive widescreen tags, the native-wide fast-path latch) is captured at record time; host overlays travel with the present; a present that a newer recorded frame already supersedes is skipped. The emulation thread answers projective/wide-present/draw-area queries from mirrors of the recorded calls. The per-frame display-ring readback is queued (fbo_peek_deferred) instead of being a sync point. The GL fixtures link render_thread.c and stub the two facade hooks; the scale fixture also gains the gpu_ws_background_requires_full_composite stub it was already missing (it failed to link on master). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
* same struct tag) so this header needs no SDL include. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "gpu_projective_clip.h"
|
||||
|
||||
struct SDL_Window;
|
||||
@@ -464,6 +465,24 @@ void gl_renderer_batch_diag(uint64_t out[8]);
|
||||
void gl_renderer_set_texture_window_batching(int on);
|
||||
int gl_renderer_get_texture_window_batching(void);
|
||||
|
||||
/* Render thread ([video] render_thread, docs/RENDER_THREAD.md). Opt-in, off
|
||||
* by default; with it off nothing below runs and the backend is unchanged.
|
||||
* start: after gl_renderer_init_context, on the thread holding the context;
|
||||
* moves the context to a new render thread and records backend calls from
|
||||
* here on (1 = running). max_frames bounds closed frames in flight. stop:
|
||||
* drain and take the context back (gl_renderer_shutdown also stops it).
|
||||
* frame_boundary: once per vblank after the present; hands the context to or
|
||||
* from the render thread and applies the in-flight bound. sync: an explicit
|
||||
* sync point for readers of renderer-written host state outside the GL entry
|
||||
* points. json: stats object body for the render_thread debug command. */
|
||||
int gl_renderer_render_thread_start(int max_frames);
|
||||
void gl_renderer_render_thread_stop(void);
|
||||
int gl_renderer_render_thread_active(void);
|
||||
void gl_renderer_render_thread_frame_boundary(void);
|
||||
void gl_renderer_render_thread_sync(const char *reason);
|
||||
int gl_renderer_render_thread_json(char *out, size_t cap);
|
||||
int gl_renderer_fbo_peek_deferred(int x, int y, int w, int h, uint16_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,7 @@ typedef enum {
|
||||
|
||||
void gr_set_backend(GrBackend backend); /* call before gr_init() */
|
||||
GrBackend gr_backend(void); /* effective backend after init */
|
||||
void gr_refresh_backend(void); /* re-fetch the GL table (render thread) */
|
||||
|
||||
/* Lifecycle / global state */
|
||||
void gr_init(uint16_t *vram);
|
||||
|
||||
@@ -12,6 +12,8 @@ extern "C" {
|
||||
|
||||
/* Initialize software renderer */
|
||||
void sw_renderer_init(uint16_t* vram);
|
||||
/* Rebind the VRAM array only; draw state is kept. */
|
||||
void sw_renderer_rebind_vram(uint16_t* vram);
|
||||
|
||||
/* Internal-resolution supersampling (SSAA).
|
||||
* scale == 1 : disabled, renderer behaves exactly as native VRAM only.
|
||||
|
||||
+848
-35
File diff suppressed because it is too large
Load Diff
@@ -137,6 +137,16 @@ void gr_set_backend(GrBackend backend) {
|
||||
|
||||
GrBackend gr_backend(void) { return g_effective; }
|
||||
|
||||
/* Re-fetch the OpenGL table after the render thread starts or stops
|
||||
* (gl_backend_get then returns the recording table, or the direct one). The
|
||||
* effective backend does not change. */
|
||||
void gr_refresh_backend(void) {
|
||||
if (g_effective == GR_BACKEND_OPENGL) {
|
||||
const GpuRenderBackend *gl = gl_backend_get();
|
||||
if (gl) g_b = gl;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Dispatch wrappers (one line each; forward to the active backend) ---- */
|
||||
void gr_init(uint16_t *vram) { g_b->init(vram); gpu_hd_textures_set_vram(vram); g_hd_offset_x = g_hd_offset_y = 0; g_hd_texture_window = 0; g_hd_semi = g_hd_perspective = 0; }
|
||||
void gr_set_scale(int scale) { g_b->set_scale(scale); }
|
||||
|
||||
@@ -481,6 +481,12 @@ void sw_renderer_init(uint16_t *vram) {
|
||||
g_texture_filter_req = 0;
|
||||
}
|
||||
|
||||
/* Point the rasterizer at another VRAM array without touching any draw
|
||||
* state (the OpenGL render thread switches its upload source this way). */
|
||||
void sw_renderer_rebind_vram(uint16_t *vram) {
|
||||
g_vram = vram;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Supersampling control */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
@@ -71,6 +71,8 @@ def main():
|
||||
sources = [("probe", fixture), ("sw", framework / "runtime/src/gpu_sw_renderer.c"),
|
||||
("rp", framework / "runtime/src/render_pass_plan.c"),
|
||||
("xr", framework / "runtime/src/psx_openxr.c")]
|
||||
if (framework / "runtime/src/render_thread.c").exists():
|
||||
sources.append(("rth", framework / "runtime/src/render_thread.c"))
|
||||
for name, source in sources:
|
||||
if run([compiler / "gcc.exe", "-std=c11", "-O2", "-flto", "-DPSX_SDL3=1",
|
||||
"-DPSX_NO_DEBUG_TOOLS=1", *includes, "-c", source,
|
||||
|
||||
@@ -134,6 +134,8 @@ def main():
|
||||
sources.append(("rp", framework / "runtime/src/render_pass_plan.c"))
|
||||
if (framework / "runtime/src/psx_openxr.c").exists():
|
||||
sources.append(("xr", framework / "runtime/src/psx_openxr.c"))
|
||||
if (framework / "runtime/src/render_thread.c").exists():
|
||||
sources.append(("rth", framework / "runtime/src/render_thread.c"))
|
||||
# Unused renderer functions reference the rest of the runtime; the linker
|
||||
# drops them (-dead_strip, or per-function sections with --gc-sections).
|
||||
# MinGW's PE linker reports undefined references from sections it later
|
||||
|
||||
@@ -41,6 +41,8 @@ def main():
|
||||
('rp',fw/'runtime/src/render_pass_plan.c')]
|
||||
if (fw/'runtime/src/psx_openxr.c').exists():
|
||||
sources.append(('xr',fw/'runtime/src/psx_openxr.c'))
|
||||
if (fw/'runtime/src/render_thread.c').exists():
|
||||
sources.append(('rth',fw/'runtime/src/render_thread.c'))
|
||||
objects=[]
|
||||
for name,source in sources:
|
||||
obj=out/(name+'.o'); run([args.cc,*flags,'-c',source,'-o',obj]); objects.append(obj)
|
||||
|
||||
@@ -16,6 +16,10 @@ void gpu_vram_dirty_mark_row_impl(uint32_t y){}
|
||||
void gpu_vram_dirty_mark_rect(int x,int y,int w,int h){}
|
||||
void gpu_vram_dirty_mark_all(void){}
|
||||
int psx_netplay_active(void){return 0;}
|
||||
/* The renderer's facade hooks (gpu_render.c) for the render thread, which
|
||||
* these fixtures never start. */
|
||||
GrBackend gr_backend(void){return GR_BACKEND_OPENGL;}
|
||||
void gr_refresh_backend(void){}
|
||||
static int test_depth24;
|
||||
int gpu_display_is_depth24(void){return test_depth24;}
|
||||
void gpu_get_display_info(GpuDisplayInfo *out){memset(out,0,sizeof(*out));out->display_x=32;out->display_y=32;out->width=320;out->height=16;}
|
||||
|
||||
@@ -88,6 +88,10 @@ void gpu_vram_dirty_mark_row_impl(uint32_t y){(void)y;}
|
||||
void gpu_vram_dirty_mark_rect(int x,int y,int w,int h){(void)x;(void)y;(void)w;(void)h;}
|
||||
void gpu_vram_dirty_mark_all(void){}
|
||||
int psx_netplay_active(void){return 0;}
|
||||
/* The renderer's facade hooks (gpu_render.c) for the render thread, which
|
||||
* these fixtures never start. */
|
||||
GrBackend gr_backend(void){return GR_BACKEND_OPENGL;}
|
||||
void gr_refresh_backend(void){}
|
||||
int gpu_display_is_depth24(void){return 0;}
|
||||
void gpu_get_display_info(GpuDisplayInfo *out){memset(out,0,sizeof(*out));out->width=320;out->height=240;}
|
||||
int psx_ws_prim_in_backdrop(void){return 0;}
|
||||
|
||||
Reference in New Issue
Block a user