(Video) Key the OSD font on the driver instance that owns it

font_driver_init_osd() decided whether to build a font by presence
alone:

    if (!video_font_driver && video_info)

which is only correct if every init is preceded by a matching free.
It is not: a reinit can abandon the old driver without its free()
running, and the font then survives holding GPU objects from a device
that is gone, whose handle values the new device recycles. The
converse fails too - a teardown arriving after the next driver is up
takes the live font with it, leaving no OSD text at all.

Record the video driver instance a font was built against, and decide
on ownership rather than presence:

  - init_osd() drops a font owned by a different instance before
    building the new one, so a skipped teardown cannot strand it
  - font_driver_free_osd_for(video_data) frees only if that instance
    still owns it, so an out-of-order teardown is a no-op

Both hooks added in the previous commit use the keyed free. The
unconditional font_driver_free_osd() stays for drivers that still
manage their own font, where init and free are adjacent by
construction.

Verified against a harness modelling the real call graph - the init
and free hooks, the wrapper's CMD_INIT/CMD_FREE, and init_osd()'s
guard - over startup, content load, SET_SYSTEM_AV_INFO reinit, a
second reinit, driver switch between a migrated and an unmigrated
driver, threading toggled across a reinit, a reinit that never reaches
free(), and adversarial orderings (init before free, double free,
double init), threaded and unthreaded. The invariant checked after
every step is that a live driver has exactly one live font built
against that same instance, that no font outlives its instance, and
that creations and frees balance. Presence-keyed fails the
skipped-teardown and out-of-order-teardown cases; ownership-keyed
passes all of them.
This commit is contained in:
O authored and libretroadmin committed 2026-08-08 03:00:34 +02:00
1 parent e652f95214
commit a725f60ce4
4 files changed
+37 -6

No files matched your search

+26 -1
View File
@@ -34,6 +34,11 @@
/* TODO/FIXME - global */
static void *video_font_driver = NULL;
/* The video driver instance video_font_driver was built against. A
* font holds GPU objects created through that instance and is not
* reusable by any other, so ownership - not mere presence - is what
* decides whether it can be kept or must be freed. */
static void *video_font_driver_owner = NULL;
/* Monotonic counter incremented whenever any font instance is
* freed. Consumers that cache per-font derived data (e.g. the
@@ -1120,10 +1125,29 @@ void font_driver_init_osd(
bool is_threaded,
enum font_driver_render_api api)
{
/* A font left over from a different instance cannot be adopted:
* its images belong to a device that is gone, whose handles the
* new one will recycle. Drop it rather than keep it. Guarding on
* presence alone is what let a stale font survive a reinit. */
if (video_font_driver && video_font_driver_owner != video_data)
font_driver_free_osd();
if (!video_font_driver && video_info)
video_font_driver = font_driver_init_first(video_data,
*video_info->path_font ? video_info->path_font : NULL,
video_info->font_size, threading_hint, is_threaded, api);
if (video_font_driver)
video_font_driver_owner = video_data;
}
void font_driver_free_osd_for(void *video_data)
{
/* Only the owner may free it. Teardown of an instance that no
* longer owns the font - a stale or deferred free - must leave the
* live one alone. */
if (video_font_driver && video_font_driver_owner == video_data)
font_driver_free_osd();
}
void font_driver_free_osd(void)
@@ -1131,5 +1155,6 @@ void font_driver_free_osd(void)
if (video_font_driver)
font_driver_free((font_data_t*)video_font_driver);
video_font_driver = NULL;
video_font_driver = NULL;
video_font_driver_owner = NULL;
}
+5
View File
@@ -149,6 +149,11 @@ void font_driver_init_osd(
void font_driver_free_osd(void);
/* Frees the OSD font only if it was built against @video_data. Use
* this from any teardown that may run out of order with respect to the
* next init. */
void font_driver_free_osd_for(void *video_data);
int font_driver_get_line_height(font_data_t *font, float scale);
int font_driver_get_line_ascender(font_data_t *font, float scale);
int font_driver_get_line_descender(font_data_t *font, float scale);
+5 -4
View File
@@ -1978,15 +1978,16 @@ void video_driver_free_internal(void)
/* Drop the OSD font before the driver that owns its GPU objects
* goes away. Only for drivers that have handed the lifecycle up;
* the rest still free it inside their own free(). Under threaded
* video the wrapper does this from CMD_FREE, on the video thread,
* so it must not also happen here. */
* the rest still free it inside their own free(). Keyed on
* ownership so that a teardown arriving after the next driver is
* already up cannot take the live font with it. Under threaded
* video the wrapper does this from CMD_FREE, on the video thread. */
#ifdef HAVE_THREADS
if (!is_threaded)
#endif
{
if (vid && vid->font_api != FONT_DRIVER_RENDER_DONT_CARE)
font_driver_free_osd();
font_driver_free_osd_for(video_st->data);
}
if (video_st->data && vid && vid->free)
+1 -1
View File
@@ -222,7 +222,7 @@ static bool video_thread_handle_packet(
* against it, and this is the thread they belong to. */
if ( thr->driver
&& thr->driver->font_api != FONT_DRIVER_RENDER_DONT_CARE)
font_driver_free_osd();
font_driver_free_osd_for(thr->driver_data);
if (thr->driver_data && thr->driver && thr->driver->free)
thr->driver->free(thr->driver_data);
thr->driver_data = NULL;