diff --git a/runtime/include/gpu_hd_textures.h b/runtime/include/gpu_hd_textures.h index 6804a70c..59e213fd 100644 --- a/runtime/include/gpu_hd_textures.h +++ b/runtime/include/gpu_hd_textures.h @@ -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. */ diff --git a/runtime/include/gpu_render.h b/runtime/include/gpu_render.h index 5f70d079..10a67e25 100644 --- a/runtime/include/gpu_render.h +++ b/runtime/include/gpu_render.h @@ -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 diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index ed04fc53..c4b29752 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -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(); diff --git a/runtime/src/gpu_hd_textures.cpp b/runtime/src/gpu_hd_textures.cpp index cb58846f..0849a63f 100644 --- a/runtime/src/gpu_hd_textures.cpp +++ b/runtime/src/gpu_hd_textures.cpp @@ -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. */ diff --git a/runtime/src/gpu_render.c b/runtime/src/gpu_render.c index c205fb80..0a0db488 100644 --- a/runtime/src/gpu_render.c +++ b/runtime/src/gpu_render.c @@ -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); } diff --git a/runtime/tests/gpu_hd_texture_stubs.inc b/runtime/tests/gpu_hd_texture_stubs.inc index ef44dac4..01b20f17 100644 --- a/runtime/tests/gpu_hd_texture_stubs.inc +++ b/runtime/tests/gpu_hd_texture_stubs.inc @@ -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 diff --git a/runtime/tests/test_gl_hd_texture_pack.c b/runtime/tests/test_gl_hd_texture_pack.c index b4bd8111..3e963a47 100644 --- a/runtime/tests/test_gl_hd_texture_pack.c +++ b/runtime/tests/test_gl_hd_texture_pack.c @@ -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(); diff --git a/runtime/tests/test_gl_scale_invariance.c b/runtime/tests/test_gl_scale_invariance.c index b1c246b4..b20092c0 100644 --- a/runtime/tests/test_gl_scale_invariance.c +++ b/runtime/tests/test_gl_scale_invariance.c @@ -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){}