(Menu/Widgets) Retire fonts rebuilt from a render path instead of freeing

XMB was not the only caller freeing a font from a path that runs before
the video driver's frame function. The same free-then-create sits in
four more places, all reached per frame:

  ozone_init_font        ozone_render -> ozone_set_layout, six fonts
  materialui_init_font   materialui_render -> materialui_layout, three
  gfx_widgets_font_init  gfx_widgets_iterate -> gfx_widgets_layout, six
  menu_screensaver       menu_screensaver_iterate, on a dimension change

All four fire on a menu scale factor change, which is the operation that
crashed D3D12 in XMB for years before 45f8bf4a. Whether they have been
crashing quietly or merely could is not something the code can answer;
the structure is the same either way.

Build the replacement before releasing the old handle and retire the old
one through font_driver_free_deferred(). The screensaver has no
replacement to build at that point — it clears the font and lets the
effect init rebuild it below — so it only needs the retire.

Building first has a second benefit at three of the four sites: a font
that fails to load now leaves the previous one in place rather than
leaving the caller with none. Ozone's init in particular returns false
on failure and its caller clears HAS_ALL_ASSETS, but it was returning
with font_data->font already NULL.

The teardown frees in these files are left alone: they run when the
context is going away, where there is no later frame to wait for and
the queue is drained wholesale.
This commit is contained in:
libretroadmin committed 2026-08-17 23:12:45 +02:00
1 parent bdd3f3e57b
commit 0e45cec310
4 files changed
+52 -26

No files matched your search

+18 -10
View File
@@ -943,19 +943,27 @@ static void gfx_widgets_font_init(
if (scaled_size < 9)
scaled_size = 9;
/* Free existing font */
if (font_data->font)
{
font_driver_free(font_data->font);
font_data->font = NULL;
}
/* Get approximate glyph width */
font_data->glyph_width = scaled_size * (3.0f / 4.0f);
/* Create font */
font_data->font = gfx_display_font_file(p_disp,
font_path, scaled_size, is_threaded);
/* Create font.
*
* Built before the old one is released, and the old one retired
* rather than freed: gfx_widgets_layout() reaches here from
* gfx_widgets_iterate(), which runs before the video driver's
* frame function, so freeing the atlas outright can pull it out
* from under a command list that still references it. */
{
font_data_t *old_font = font_data->font;
font_data->font = gfx_display_font_file(p_disp,
font_path, scaled_size, is_threaded);
if (!font_data->font)
font_data->font = old_font;
else if (old_font)
font_driver_free_deferred(old_font);
}
/* Get font metadata. gfx_display_font_file() can fail, and there is
* no implicit font to fall back on any more, so the approximate
+12 -6
View File
@@ -9451,17 +9451,12 @@ static void materialui_init_font(gfx_display_t *p_disp,
const char *wideglyph_str = msg_hash_get_wideglyph_str();
settings_t *settings = config_get_ptr();
const char *dir_assets = settings->paths.directory_assets;
font_data_t *old_font = font_data->font;
fontpath[0] = '\0';
/* We assume the average glyph aspect ratio is close to 3:4 */
font_data->glyph_width = (int)((font_size * (3.0f / 4.0f)) + 0.5f);
if (font_data->font)
{
font_driver_free(font_data->font);
font_data->font = NULL;
}
{
const char *lang_font = font_driver_language_font_file();
@@ -9478,9 +9473,20 @@ static void materialui_init_font(gfx_display_t *p_disp,
strlcpy(fontpath, default_fontpath, sizeof(fontpath));
}
/* Built before the old one is released, and the old one retired
* rather than freed: materialui_layout() reaches here from
* materialui_render(), which runs before the video driver's frame
* function, so freeing the atlas outright can pull it out from
* under a command list that still references it. Building first
* also keeps the previous font in place if this fails. */
font_data->font = gfx_display_font_file(p_disp,
fontpath, font_size, video_is_threaded);
if (!font_data->font)
font_data->font = old_font;
else if (old_font)
font_driver_free_deferred(old_font);
/* Follow the menu language, so a language change can rebuild this
* where it stands instead of asking for a restart. */
font_driver_set_language_font(font_data->font, pkg_dir, default_fontpath);
+16 -8
View File
@@ -10002,13 +10002,7 @@ static bool ozone_init_font(
int glyph_width = 0;
gfx_display_t *p_disp = disp_get_ptr();
const char *wideglyph_str = msg_hash_get_wideglyph_str();
/* Free existing */
if (font_data->font)
{
font_driver_free(font_data->font);
font_data->font = NULL;
}
font_data_t *old_font = font_data->font;
/* Enforce minimum readable font size for small screens */
if (font_size < 9)
@@ -10018,9 +10012,23 @@ static bool ozone_init_font(
font_data->line_height = (int)(font_size + 0.5f);
font_data->glyph_width = (int)((font_size * (3.0f / 4.0f)) + 0.5f);
/* Create font */
/* Create font.
*
* Built before the old one is released, and the old one retired
* rather than freed: ozone_set_layout() reaches here from
* ozone_render(), which runs before the video driver's frame
* function, so freeing the atlas outright can pull it out from
* under a command list that still references it. Building first
* also means a failure below leaves the previous font in place
* instead of leaving Ozone with none. */
if (!(font_data->font = gfx_display_font_file(p_disp, font_path, font_size, is_threaded)))
{
font_data->font = old_font;
return false;
}
if (old_font)
font_driver_free_deferred(old_font);
/* Get font metadata */
if ((glyph_width = font_driver_get_message_width(font_data->font, "a", 1, 1.0f)) > 0)
+6 -2
View File
@@ -460,10 +460,14 @@ static bool menu_screensaver_update_state(
{
menu_screensaver_set_dimensions(screensaver, width, height);
/* Free any existing font */
/* Retire any existing font. This runs from
* menu_screensaver_iterate(), before the video driver's frame
* function, and the font is rebuilt below, so releasing it
* outright can pull the atlas out from under a command list
* that still references it. */
if (screensaver->font_data.font)
{
font_driver_free(screensaver->font_data.font);
font_driver_free_deferred(screensaver->font_data.font);
video_coord_array_free(&screensaver->font_data.raster_block.carr);
screensaver->font_data.font = NULL;
}