mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Render thread: HD texture residency in command order
HD texture packs matched draws on the render thread but tracked uploads and invalidations on the emulation thread, hashing gpu.c's array: a data race (crash seen in hd_texture_pack_match) and wrong identities whenever the emulation thread had already recorded a later upload. The facade now sends residency changes through an optional backend hook (hd_texture_note). The render-thread backend records them as RTH_HD_NOTE after the upload/draw they follow; the replay hashes the render thread's own VRAM copy, which the HD module now follows with the SW rasterizer (gpu_hd_textures_bind_vram). Pack (re)configuration and HD mode changes are sync points. Other backends apply the change immediately as before. The HD GL fixture gains a render-thread case (fails without this change) and links again: the scale fixture's facade stubs no longer collide with the real gpu_render.c it includes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,11 @@ void gpu_hd_textures_observe_draw(uint16_t texpage, uint16_t clut_x,
|
||||
/* Native VRAM and residency hooks. A full restage resets identity rather than
|
||||
* pretending a savestate is an original texture upload. */
|
||||
void gpu_hd_textures_set_vram(const uint16_t* vram);
|
||||
/* Move to another copy of the same native VRAM (the GL render thread's
|
||||
* private copy and gpu.c's array match at every hand-off); keeps residency. */
|
||||
void gpu_hd_textures_bind_vram(const uint16_t* vram);
|
||||
/* Residency is hashed from the bound native VRAM after the write; words may
|
||||
* be NULL (a replayed upload whose payload already reached that VRAM). */
|
||||
void gpu_hd_textures_track_upload(int x, int y, int width_words, int height,
|
||||
const uint16_t* words);
|
||||
/* Header-before-payload invalidation and pre/post native copy observations. */
|
||||
|
||||
@@ -214,8 +214,15 @@ typedef struct GpuRenderBackend {
|
||||
* pixel count, writes width/height to ow/oh. NULL if unsupported. */
|
||||
int (*wide_dump_full)(uint32_t *out, int cap_pixels, int *ow, int *oh,
|
||||
int base_x);
|
||||
/* HD texture-pack residency (gpu_hd_textures.h) in command order with the
|
||||
* backend's own VRAM: op GR_HD_NOTE_*; sx/sy are the copy source for
|
||||
* GR_HD_NOTE_BEGIN_COPY. NULL = apply it immediately. */
|
||||
void (*hd_texture_note)(int op, int x, int y, int w, int h, int sx, int sy);
|
||||
} GpuRenderBackend;
|
||||
|
||||
enum { GR_HD_NOTE_INVALIDATE = 0, GR_HD_NOTE_TRACK_UPLOAD = 1, GR_HD_NOTE_BEGIN_UPLOAD = 2,
|
||||
GR_HD_NOTE_BEGIN_COPY = 3, GR_HD_NOTE_END_COPY = 4 };
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -447,12 +447,18 @@ enum {
|
||||
RTH_WIDE_DISABLE, RTH_WIDE_CLEAR, RTH_WIDE_CLEAR_MARGINS, RTH_PROJ_TRI,
|
||||
RTH_WIDE_RECOVERY, RTH_INTERP_SUSPENDED, RTH_PRESENT_VRAM,
|
||||
RTH_PRESENT_WIDE, RTH_STATE, RTH_PEEK, RTH_RING_CAPTURE, RTH_DYN_STEP,
|
||||
RTH_FRAME, RTH_FG_SRC
|
||||
RTH_FRAME, RTH_FG_SRC, RTH_HD_NOTE
|
||||
};
|
||||
static int rth_record_mode(void);
|
||||
/* Render-thread frame cost (dynamic resolution; defined with GL_RT_BACKEND). */
|
||||
static uint64_t s_rthf_swap_ns = 0; /* render thread: time in the swap */
|
||||
static uint64_t host_now_ns_rthf(void);
|
||||
/* The SW rasterizer and the HD texture residency read the same native VRAM
|
||||
* copy as the GL backend (gpu.c's array, or the render thread's private one). */
|
||||
static void rth_rebind_vram(uint16_t *vram) {
|
||||
sw_renderer_rebind_vram(vram);
|
||||
gpu_hd_textures_bind_vram(vram);
|
||||
}
|
||||
static int rth_rec_ints(uint16_t op, uint16_t flags, int n, const int32_t *v);
|
||||
static uint16_t rth_prim_flags(void);
|
||||
static int rth_record_present(uint16_t op, int n, const int32_t *v);
|
||||
@@ -2936,6 +2942,8 @@ static size_t s_hd_gl_cache_bytes;
|
||||
static uint64_t s_hd_gl_cache_clock;
|
||||
|
||||
void gl_renderer_clear_hd_texture_cache(void) {
|
||||
/* Pack (re)configuration: the render thread must be idle before the
|
||||
* session and its replacement textures change. */
|
||||
GL_RT_SYNC("clear_hd_texture_cache");
|
||||
flush_flat_batch(); flush_tex_batch(); hiw_flush_queue();
|
||||
for (int i = 0; i < HD_GL_CACHE_CAP; ++i) {
|
||||
@@ -5694,9 +5702,7 @@ void gl_renderer_set_cpu_auth_dual(int on) {
|
||||
}
|
||||
|
||||
void gl_renderer_set_hd_texture_mode(int on) {
|
||||
/* Residency and CPU-authoritative VRAM stay on the emulation thread.
|
||||
* Drain the GPU-authoritative stream before entering that mode; the
|
||||
* eligibility gate below keeps the render thread parked until HD is off. */
|
||||
/* Drain the GPU-authoritative stream before switching authority. */
|
||||
GL_RT_SYNC("set_hd_texture_mode");
|
||||
on = on ? 1 : 0;
|
||||
if (on == s_hd_native_authority) return;
|
||||
@@ -9832,7 +9838,7 @@ static void gl_rth_acquire(const char *reason) {
|
||||
if (!s_rth_on || rt_on_render_thread() || rt_held()) return;
|
||||
rt_acquire(reason);
|
||||
s_vram = s_rth_vram_pub;
|
||||
sw_renderer_rebind_vram(s_rth_vram_pub);
|
||||
rth_rebind_vram(s_rth_vram_pub);
|
||||
}
|
||||
|
||||
static void gl_rth_release(void) {
|
||||
@@ -9841,7 +9847,7 @@ static void gl_rth_release(void) {
|
||||
* thread continues from a copy of it. */
|
||||
memcpy(s_rth_vram_priv, s_rth_vram_pub, (size_t)VRAM_W * VRAM_H * sizeof(uint16_t));
|
||||
s_vram = s_rth_vram_priv;
|
||||
sw_renderer_rebind_vram(s_rth_vram_priv);
|
||||
rth_rebind_vram(s_rth_vram_priv);
|
||||
rth_mirror_resync();
|
||||
rt_release();
|
||||
}
|
||||
@@ -11427,7 +11433,7 @@ static void rtb_init(uint16_t *vram) {
|
||||
GL_RT_SYNC("init");
|
||||
if (s_rth_on) s_rth_vram_pub = vram;
|
||||
glb_init(vram);
|
||||
if (s_rth_on && !rt_held()) sw_renderer_rebind_vram(s_rth_vram_priv);
|
||||
if (s_rth_on && !rt_held()) rth_rebind_vram(s_rth_vram_priv);
|
||||
}
|
||||
static void rtb_set_scale(int sc) { GL_RT_SYNC("set_scale"); glb_set_scale(sc); }
|
||||
/* The scale changes under a sync point, or by a recorded dynamic-resolution
|
||||
@@ -11533,6 +11539,18 @@ static void rtb_vram_transfer_in(int x, int y, int w, int h, const uint16_t *d)
|
||||
}
|
||||
glb_vram_transfer_in(x, y, w, h, d);
|
||||
}
|
||||
static void hd_note_exec(int op, int x, int y, int w, int h, int sx, int sy) {
|
||||
switch (op) {
|
||||
case GR_HD_NOTE_TRACK_UPLOAD: gpu_hd_textures_track_upload(x, y, w, h, NULL); break;
|
||||
case GR_HD_NOTE_BEGIN_UPLOAD: gpu_hd_textures_begin_upload(x, y, w, h); break;
|
||||
case GR_HD_NOTE_BEGIN_COPY: gpu_hd_textures_begin_copy(sx, sy, x, y, w, h); break;
|
||||
case GR_HD_NOTE_END_COPY: gpu_hd_textures_end_copy(); break;
|
||||
default: gpu_hd_textures_invalidate(x, y, w, h); break;
|
||||
}
|
||||
}
|
||||
/* After the upload/draw it follows, in command order, against s_vram. */
|
||||
static void rtb_hd_texture_note(int op, int x, int y, int w, int h, int sx, int sy) {
|
||||
RTH_DIRECT_OR(RTH_REC(RTH_HD_NOTE, 0, op, x, y, w, h, sx, sy)); hd_note_exec(op, x, y, w, h, sx, sy); }
|
||||
static void rtb_vram_transfer_out(int x, int y, int w, int h, uint16_t *d) {
|
||||
GL_RT_SYNC("vram_transfer_out"); glb_vram_transfer_out(x, y, w, h, d); }
|
||||
static void rtb_set_draw_area(int x1, int y1, int x2, int y2) {
|
||||
@@ -11635,6 +11653,7 @@ static const GpuRenderBackend GL_RT_BACKEND = {
|
||||
.wide_clear_margins = rtb_wide_clear_margins,
|
||||
.render_wide_display = rtb_render_wide_display,
|
||||
.wide_dump_full = rtb_wide_dump_full,
|
||||
.hd_texture_note = rtb_hd_texture_note,
|
||||
};
|
||||
|
||||
/* ---- replay (render thread) ---------------------------------------------- */
|
||||
@@ -11693,6 +11712,7 @@ static void gl_rth_exec(void *user, const RtCmd *c, const void *payload) {
|
||||
break;
|
||||
case RTH_VRAM_WRITE: glb_vram_write(v[0], v[1], (uint16_t)v[2]); break;
|
||||
case RTH_XFER_IN: glb_vram_transfer_in(v[0], v[1], v[2], v[3], (const uint16_t *)(v + 4)); break;
|
||||
case RTH_HD_NOTE: hd_note_exec(v[0], v[1], v[2], v[3], v[4], v[5], v[6]); break;
|
||||
case RTH_AREA: glb_set_draw_area(v[0], v[1], v[2], v[3]); break;
|
||||
case RTH_OFFSET: glb_set_draw_offset(v[0], v[1]); break;
|
||||
case RTH_WIDE_CONFIGURE: glb_wide_configure(v[0], v[1]); break;
|
||||
@@ -11773,7 +11793,7 @@ int gl_renderer_render_thread_start(int max_frames) {
|
||||
s_rthe_flat_bd = s_rthe_vp_w = s_rthe_bg_full = -1;
|
||||
rth_mirror_resync();
|
||||
s_vram = priv;
|
||||
sw_renderer_rebind_vram(priv);
|
||||
rth_rebind_vram(priv);
|
||||
s_rth_on = 1;
|
||||
RtConfig cfg;
|
||||
cfg.ring_bytes = (size_t)32u << 20;
|
||||
@@ -11788,7 +11808,7 @@ int gl_renderer_render_thread_start(int max_frames) {
|
||||
pt_end();
|
||||
s_rth_on = 0;
|
||||
s_vram = s_rth_vram_pub;
|
||||
sw_renderer_rebind_vram(s_rth_vram_pub);
|
||||
rth_rebind_vram(s_rth_vram_pub);
|
||||
free(priv);
|
||||
s_rth_vram_priv = NULL;
|
||||
return 0;
|
||||
@@ -11803,7 +11823,7 @@ void gl_renderer_render_thread_stop(void) {
|
||||
pt_end();
|
||||
s_rth_on = 0;
|
||||
s_vram = s_rth_vram_pub;
|
||||
sw_renderer_rebind_vram(s_rth_vram_pub);
|
||||
rth_rebind_vram(s_rth_vram_pub);
|
||||
free(s_rth_vram_priv);
|
||||
s_rth_vram_priv = NULL;
|
||||
gr_refresh_backend();
|
||||
|
||||
@@ -267,6 +267,9 @@ extern "C" int gpu_hd_textures_configure(const char* root, int replacements,
|
||||
fail(error, capacity, dump_error); return 0;
|
||||
}
|
||||
}
|
||||
/* Quiesce renderer users (GL render thread) before reading the old
|
||||
* session's residency; the cache is rebuilt from the new session. */
|
||||
if (session) gl_renderer_clear_hd_texture_cache();
|
||||
if (session && (session->replacements || session->dump) && session->root == next->root) {
|
||||
if (!duck_texture_pack_copy_tracking(next->duck, session->duck)) {
|
||||
fail(error, capacity, "Could not preserve texture-upload tracking during reload."); return 0;
|
||||
@@ -353,6 +356,7 @@ extern "C" void gpu_hd_textures_get_diag(GpuHdTextureDiag* out) {
|
||||
out->diagnostic = session->diagnostics.back().c_str();
|
||||
}
|
||||
extern "C" void gpu_hd_textures_note_applied(void) { if (session) ++session->diag.applied_draws; }
|
||||
extern "C" void gpu_hd_textures_bind_vram(const uint16_t* vram) { native_vram = vram; }
|
||||
extern "C" void gpu_hd_textures_set_vram(const uint16_t* vram) { native_vram = vram; gpu_hd_textures_reset_tracking(); }
|
||||
extern "C" void gpu_hd_textures_reset_tracking(void) {
|
||||
if (!session) return;
|
||||
@@ -391,7 +395,8 @@ extern "C" void gpu_hd_textures_invalidate(int x, int y, int width, int height)
|
||||
}
|
||||
extern "C" void gpu_hd_textures_track_upload(int x, int y, int width, int height,
|
||||
const uint16_t* words) {
|
||||
if (!session || !native_vram || !words || width < 1 || width > 1024 || height < 1 || height > 512) return;
|
||||
(void)words;
|
||||
if (!session || !native_vram || width < 1 || width > 1024 || height < 1 || height > 512) return;
|
||||
if (width == 1024 && height == 512) { gpu_hd_textures_reset_tracking(); return; }
|
||||
/* gpu.c's A0 staging already contains post-mask words. Read the canonical
|
||||
* mirror here also for host-initiated transfers which use raw input. */
|
||||
|
||||
@@ -86,6 +86,16 @@ static void hd_observe_rect(int u0,int v0,int u1,int v1,uint16_t cx,uint16_t cy,
|
||||
gpu_hd_textures_observe_draw(tp,cx,cy,limits,g_hd_texture_window,g_hd_semi);
|
||||
}
|
||||
|
||||
/* A backend that replays commands on another thread (the GL render thread)
|
||||
* owns its VRAM copy there, so residency changes travel with its commands. */
|
||||
static void hd_note(int op, int x, int y, int w, int h, const uint16_t *words) {
|
||||
if (!gpu_hd_textures_active()) return;
|
||||
if (g_b->hd_texture_note) { g_b->hd_texture_note(op, x, y, w, h, 0, 0); return; }
|
||||
if (op == GR_HD_NOTE_TRACK_UPLOAD) gpu_hd_textures_track_upload(x, y, w, h, words);
|
||||
else if (op == GR_HD_NOTE_BEGIN_UPLOAD) gpu_hd_textures_begin_upload(x, y, w, h);
|
||||
else gpu_hd_textures_invalidate(x, y, w, h);
|
||||
}
|
||||
|
||||
/* Residency describes guest native words, so any draw/copy which can touch
|
||||
* an upload ends its identity. Run after submission: an overlapping texture
|
||||
* draw must resolve its source before the destination invalidates it. */
|
||||
@@ -98,7 +108,7 @@ static void hd_invalidate_draw(int x0, int y0, int x1, int y1) {
|
||||
if (x0 < ax0) x0 = ax0; if (x1 > ax1) x1 = ax1;
|
||||
if (y0 < ay0) y0 = ay0; if (y1 > ay1) y1 = ay1;
|
||||
if (x0 <= x1 && y0 <= y1)
|
||||
gpu_hd_textures_invalidate(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
|
||||
hd_note(GR_HD_NOTE_INVALIDATE, x0, y0, x1 - x0 + 1, y1 - y0 + 1, NULL);
|
||||
}
|
||||
static void hd_invalidate_triangle(int x0, int y0, int x1, int y1, int x2, int y2) {
|
||||
int xmin = x0, xmax = x0, ymin = y0, ymax = y0;
|
||||
@@ -148,7 +158,7 @@ void gr_refresh_backend(void) {
|
||||
}
|
||||
|
||||
/* ---- 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_init(uint16_t *vram) { gpu_hd_textures_set_vram(vram); g_b->init(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); }
|
||||
int gr_scale(void) { return g_b->scale(); }
|
||||
void gr_set_texture_filter(int bilinear) { g_b->set_texture_filter(bilinear); }
|
||||
@@ -167,9 +177,16 @@ void gr_set_perspective_triangle(int enabled, float q0, float q1, float q2) {
|
||||
if (g_b->set_perspective_triangle)
|
||||
g_b->set_perspective_triangle(enabled, q0, q1, q2);
|
||||
}
|
||||
void gr_fill_rect(int x, int y, int w, int h, uint16_t c) { g_b->fill_rect(x, y, w, h, c); gpu_hd_textures_invalidate(x, y, w, h); }
|
||||
void gr_fill_rect(int x, int y, int w, int h, uint16_t c) { g_b->fill_rect(x, y, w, h, c); hd_note(GR_HD_NOTE_INVALIDATE, x, y, w, h, NULL); }
|
||||
void gr_copy_rect(int sx, int sy, int dx, int dy, int w, int h) {
|
||||
if (!gpu_hd_textures_active()) { g_b->copy_rect(sx, sy, dx, dy, w, h); return; }
|
||||
if (g_b->hd_texture_note) {
|
||||
/* Pre/post copy observations against the backend's own VRAM. */
|
||||
g_b->hd_texture_note(GR_HD_NOTE_BEGIN_COPY, dx, dy, w, h, sx, sy);
|
||||
g_b->copy_rect(sx, sy, dx, dy, w, h);
|
||||
g_b->hd_texture_note(GR_HD_NOTE_END_COPY, 0, 0, 0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (g_effective == GR_BACKEND_VULKAN) g_b->vram_read(0,0);
|
||||
gpu_hd_textures_begin_copy(sx,sy,dx,dy,w,h);
|
||||
g_b->copy_rect(sx, sy, dx, dy, w, h);
|
||||
@@ -231,10 +248,10 @@ int gr_render_display(uint32_t *o, int p, int dx, int dy, int dw, int dh) {
|
||||
int gr_render_display_hires(uint32_t *o, int p, int dx, int dy, int dw, int dh) {
|
||||
return g_b->render_display_hires(o, p, dx, dy, dw, dh);
|
||||
}
|
||||
void gr_vram_upload_begin(int x, int y, int w, int h) { gpu_hd_textures_begin_upload(x,y,w,h); }
|
||||
void gr_vram_write(int x, int y, uint16_t pixel) { g_b->vram_write(x, y, pixel); gpu_hd_textures_invalidate(x,y,1,1); }
|
||||
void gr_vram_upload_begin(int x, int y, int w, int h) { hd_note(GR_HD_NOTE_BEGIN_UPLOAD, x, y, w, h, NULL); }
|
||||
void gr_vram_write(int x, int y, uint16_t pixel) { g_b->vram_write(x, y, pixel); hd_note(GR_HD_NOTE_INVALIDATE, x, y, 1, 1, NULL); }
|
||||
uint16_t gr_vram_read(int x, int y) { return g_b->vram_read(x, y); }
|
||||
void gr_vram_transfer_in(int x, int y, int w, int h, const uint16_t *d) { g_b->vram_transfer_in(x, y, w, h, d); gpu_hd_textures_track_upload(x,y,w,h,d); }
|
||||
void gr_vram_transfer_in(int x, int y, int w, int h, const uint16_t *d) { g_b->vram_transfer_in(x, y, w, h, d); hd_note(GR_HD_NOTE_TRACK_UPLOAD, x, y, w, h, d); }
|
||||
void gr_vram_transfer_out(int x, int y, int w, int h, uint16_t *d) { g_b->vram_transfer_out(x, y, w, h, d); }
|
||||
void gr_set_draw_area(int x1, int y1, int x2, int y2){ g_b->set_draw_area(x1, y1, x2, y2); }
|
||||
void gr_get_draw_area(int *x1, int *y1, int *x2, int *y2) { g_b->get_draw_area(x1, y1, x2, y2); }
|
||||
|
||||
@@ -11,4 +11,5 @@ void gpu_hd_textures_release_image(GpuHdTextureImage* image) {
|
||||
}
|
||||
void gpu_hd_textures_note_applied(void) {}
|
||||
void gpu_hd_textures_reset_tracking(void) {}
|
||||
void gpu_hd_textures_bind_vram(const uint16_t* vram) { (void)vram; }
|
||||
#endif
|
||||
|
||||
@@ -396,69 +396,26 @@ int main(int argc,char** argv) {
|
||||
"Beetle image preserves native draw RGB and source STP");
|
||||
gr_draw_textured_rect(8,4,4,4,4,0,0,0,texture_page); capture();
|
||||
check(sample(44,16)[1]>200,"Beetle native transparent zero still cuts out replacement");
|
||||
/* HD residency stays synchronous: startup refuses the combination, and
|
||||
* live activation parks an existing thread before switching authority. */
|
||||
check(!gl_renderer_render_thread_start(2),"HD replacements refuse render-thread startup");
|
||||
/* Keep the negative-control run safe when checking an unfixed renderer. */
|
||||
if(gl_renderer_render_thread_active()) gl_renderer_render_thread_stop();
|
||||
gpu_hd_textures_shutdown();
|
||||
check(gl_renderer_render_thread_start(2),"render thread starts with HD disabled");
|
||||
gl_renderer_set_frame_generation(1);
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
state(); gr_fill_rect(96,96,4,1,0x1234);
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
check(gpu_hd_textures_configure(beetle_root,1,0,error,sizeof(error)),"HD opens while render thread is running");
|
||||
check(rt_held(),"HD activation takes and holds the GL context");
|
||||
check(vram[96*1024+96]==0x1234,"HD activation retains the queued native draw");
|
||||
char fg_diag[4096]; gl_renderer_frame_gen_json(fg_diag,sizeof(fg_diag));
|
||||
check(strstr(fg_diag,"\"active\":0")!=NULL,"Smooth motion is inactive under HD authority");
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
check(rt_held(),"HD replacement mode remains synchronous across frames");
|
||||
state(); gr_fill_rect(128,96,4,1,0x2345);
|
||||
gr_vram_upload_begin(128,96,4,1);
|
||||
vram[96*1024+128]=0x7c00; /* A partial GP0(A0) payload, as gpu.c writes it. */
|
||||
uint16_t partial_read=0; gr_vram_transfer_out(129,96,1,1,&partial_read);
|
||||
check(vram[96*1024+128]==0x7c00 && partial_read==0x2345,
|
||||
"partial upload preserves payload and preceding native draws");
|
||||
check(gpu_hd_textures_reload(error,sizeof(error)),"HD reload is safe while the thread is parked");
|
||||
check(rt_held() && vram[96*1024+128]==0x7c00 && vram[96*1024+129]==0x2345,
|
||||
"HD reload retains partial-upload VRAM");
|
||||
/* Aborted uploads have no extra private-copy tracking to leave behind. */
|
||||
gr_init(vram); state(); gr_fill_rect(128,96,4,1,0x3456);
|
||||
gr_vram_transfer_out(128,96,1,1,&partial_read);
|
||||
check(partial_read==0x3456,"reset after an incomplete upload keeps native draws visible");
|
||||
gr_set_mask_bits(1,0); gr_draw_flat_rect(192,96,4,1,0x0421);
|
||||
gr_set_mask_bits(0,1); gr_vram_upload_begin(192,96,4,1);
|
||||
check(gr_vram_read(192,96)==0x8421,
|
||||
"A0 mask check sees the preceding masked native draw");
|
||||
state();
|
||||
gpu_hd_textures_shutdown();
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
check(!rt_held(),"render thread resumes after HD replacements are disabled");
|
||||
state(); gr_fill_rect(160,96,4,1,0x4567);
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
gr_vram_transfer_out(160,96,1,1,&partial_read);
|
||||
check(partial_read==0x4567 && vram[96*1024+160]==0x4567,
|
||||
"resumed rendering retains native readbacks");
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
check(gpu_hd_textures_configure(argv[1],0,1,error,sizeof(error)),"dump-only session opens with render thread running");
|
||||
check(rt_held(),"texture dumping also parks the render thread");
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
check(rt_held(),"dump-only mode remains synchronous across frames");
|
||||
gr_vram_transfer_in(512,0,4,4,source_words);
|
||||
gr_draw_textured_rect(8,8,4,4,0,0,0,0,texture_page); capture();
|
||||
check(vram[8*1024+8]==source_words[0],"dump-only draw retains native VRAM");
|
||||
gpu_hd_textures_set_dump_enabled(0);
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
check(!rt_held(),"render thread resumes when dumping is disabled");
|
||||
gpu_hd_textures_shutdown();
|
||||
gl_renderer_set_frame_generation(0);
|
||||
check(gpu_hd_textures_configure(beetle_root,1,0,error,sizeof(error)),"HD session restored for teardown checks");
|
||||
state(); gr_fill_rect(224,96,4,1,0x5678);
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
/* Render thread: residency follows the recorded command stream and the
|
||||
* render thread's VRAM copy. The emulation thread writes its array and
|
||||
* records the overwrite before the first draw has been replayed; that
|
||||
* draw must still see the upload it was recorded after. */
|
||||
check(gl_renderer_render_thread_start(2)==1,"render thread started with an HD session");
|
||||
for(int frame=0;frame<3;++frame) {
|
||||
state(); gr_fill_rect(0,0,16,16,0x03e0);
|
||||
for(int i=0;i<16;++i) vram[i/4*1024+512+i%4]=source_words[i];
|
||||
gr_vram_transfer_in(512,0,4,4,source_words);
|
||||
gr_draw_textured_rect(4,4,4,4,0,0,0,0,texture_page);
|
||||
for(int i=0;i<16;++i) vram[i/4*1024+512+i%4]=overwrite[i];
|
||||
gr_vram_transfer_in(512,0,4,4,overwrite);
|
||||
gr_draw_textured_rect(8,4,4,4,0,0,0,0,texture_page);
|
||||
gl_renderer_render_thread_frame_boundary();
|
||||
}
|
||||
capture();
|
||||
check(sample(16,16)[0]>200 && sample(16,16)[1]<20,"render thread: replacement drawn from the upload recorded before it");
|
||||
check(sample(32,16)[2]>200 && sample(32,16)[0]<20,"render thread: later overwrite draws the new native source");
|
||||
gl_renderer_render_thread_stop();
|
||||
check(!gl_renderer_render_thread_active(),"thread stops safely after HD transitions");
|
||||
check(vram[96*1024+224]==0x5678,"stopping the parked thread preserves final HD native draws");
|
||||
check(!gl_renderer_render_thread_active(),"render thread stopped");
|
||||
memcpy(reference,vram,sizeof(vram));
|
||||
gl_renderer_set_cpu_auth_dual(1);
|
||||
gpu_hd_textures_shutdown();
|
||||
|
||||
@@ -89,7 +89,7 @@ void gpu_vram_dirty_mark_rect(int x,int y,int w,int h){(void)x;(void)y;(void)w;(
|
||||
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. */
|
||||
* these fixtures never start. The HD fixture links the real facade. */
|
||||
#ifndef PSX_TEST_HD_TEXTURE_PACK
|
||||
GrBackend gr_backend(void){return GR_BACKEND_OPENGL;}
|
||||
void gr_refresh_backend(void){}
|
||||
|
||||
Reference in New Issue
Block a user