Overlay draw callback: publish fn+ctx atomically; link it in GL fixtures

Review (#599): fn and ctx were two separate stores read on the render
thread, so an unregister could pair a live fn with a NULL ctx. They are now
one immutable record behind an acquire/release atomic pointer. The GL
fixture runners compile a fixed source list; gpu_gl_renderer.c now calls
psx_host_overlay_draw, so every run_gl_*.py links host_overlay.c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Shokunin
2026-10-09 00:25:21 -07:00
parent 153279824a
commit 8b003a591d
6 changed files with 42 additions and 9 deletions
+27 -9
View File
@@ -1,7 +1,9 @@
/* host_overlay.c -- see host_overlay.h. */
#include "host_overlay.h"
#include <stdatomic.h>
#include <stddef.h>
#include <stdlib.h>
/* ---- P1: host pause ------------------------------------------------------ */
#define PSX_HOST_PAUSE_MAX 8
@@ -29,19 +31,35 @@ const char *psx_host_pause_reason(void) {
void psx_host_pause_set_refuse_probe(PsxHostPauseRefuseFn fn) { s_pause_refuse = fn; }
/* ---- P2: overlay draw callback -------------------------------------------- */
/* Written on the main thread before the overlay can open, read on the GL
* thread; a pointer-sized store, and registration happens once at startup. */
static PsxHostOverlayDrawFn volatile s_draw_fn = NULL;
static void *volatile s_draw_ctx = NULL;
/* Registered on the main thread, read on the render / present thread. The
* function and its context are published together as one immutable record
* behind an atomic pointer (release on store, acquire on load), so a reader
* can never pair a live function with a stale or NULL context. Records are
* never freed: registration happens a handful of times per process, and a
* reader may still hold the previous record when it is replaced. */
typedef struct {
PsxHostOverlayDrawFn fn;
void *ctx;
} PsxHostOverlayDraw;
static _Atomic(PsxHostOverlayDraw *) s_draw = NULL;
void psx_host_overlay_set_draw_cb(PsxHostOverlayDrawFn fn, void *ctx) {
s_draw_ctx = ctx;
s_draw_fn = fn;
PsxHostOverlayDraw *rec = NULL;
if (fn) {
rec = (PsxHostOverlayDraw *)malloc(sizeof(*rec));
if (!rec) return;
rec->fn = fn;
rec->ctx = ctx;
}
atomic_store_explicit(&s_draw, rec, memory_order_release);
}
int psx_host_overlay_has_draw(void) { return s_draw_fn != NULL; }
int psx_host_overlay_has_draw(void) {
return atomic_load_explicit(&s_draw, memory_order_acquire) != NULL;
}
void psx_host_overlay_draw(int width, int height) {
PsxHostOverlayDrawFn fn = s_draw_fn;
if (fn && width > 0 && height > 0) fn(width, height, s_draw_ctx);
const PsxHostOverlayDraw *rec = atomic_load_explicit(&s_draw, memory_order_acquire);
if (rec && rec->fn && width > 0 && height > 0) rec->fn(width, height, rec->ctx);
}
+3
View File
@@ -95,6 +95,9 @@ def main():
if WINDOWS:
sections.append("-flto")
objs = []
# The renderer calls the in-game overlay hook (host_overlay.c) at present.
if (src / "host_overlay.c").exists():
sources.append(("hov", src / "host_overlay.c"))
for name, path in sources:
o = dest / (name + ".o")
r = run([args.cc, "-std=gnu11", "-O1", "-DPSX_SDL3=1", "-DPSX_NO_DEBUG_TOOLS=1",
+3
View File
@@ -77,6 +77,9 @@ def main():
sources.append(("pth", framework / "runtime/src/present_thread.c"))
if (framework / "runtime/src/frame_gen.c").exists():
sources.append(("fg", framework / "runtime/src/frame_gen.c"))
# The renderer calls the in-game overlay hook (host_overlay.c) at present.
if (framework / "runtime/src" / "host_overlay.c").exists():
sources.append(("hov", framework / "runtime/src" / "host_overlay.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,
+3
View File
@@ -94,6 +94,9 @@ def main():
if WINDOWS:
sections.append("-flto")
objs = []
# The renderer calls the in-game overlay hook (host_overlay.c) at present.
if (src / "host_overlay.c").exists():
sources.append(("hov", src / "host_overlay.c"))
for name, path in sources:
o = dest / (name + ".o")
r = run([args.cc, "-std=gnu11", "-O1", "-DPSX_SDL3=1", "-DPSX_NO_DEBUG_TOOLS=1",
+3
View File
@@ -154,6 +154,9 @@ def main():
if passes:
sections.append("-DPSX_TEST_RENDER_PASSES=1")
objs = []
# The renderer calls the in-game overlay hook (host_overlay.c) at present.
if (framework / "runtime/src" / "host_overlay.c").exists():
sources.append(("hov", framework / "runtime/src" / "host_overlay.c"))
for name, src in sources:
o = dest / (name + ".o")
r = run([args.cc, "-std=gnu11", "-O1", "-DPSX_SDL3=1", "-DPSX_NO_DEBUG_TOOLS=1",
+3
View File
@@ -52,6 +52,9 @@ def main():
if (fw/'runtime/src/frame_gen.c').exists():
sources.append(('fg',fw/'runtime/src/frame_gen.c'))
objects=[]
# The renderer calls the in-game overlay hook (host_overlay.c) at present.
if (fw/'runtime/src' / "host_overlay.c").exists():
sources.append(("hov", fw/'runtime/src' / "host_overlay.c"))
for name,source in sources:
obj=out/(name+'.o'); run([args.cc,*flags,'-c',source,'-o',obj]); objects.append(obj)
if args.hd_pack: