(Font) Recompute the derived metrics when a font is rebuilt

font_driver_reload_fonts() swaps a font's renderer state in place, so
callers keep working - but the values derived from it do not follow.
The menu drivers cache glyph_width, wideglyph_width, line_height,
line_ascender and line_centre_offset in font_data_impl_t at font
creation, and a rebuilt font has a different face and so different
metrics. Left alone that gives the right glyphs at the wrong spacing.

The signal already existed: font_driver_generation moves whenever a
font is freed or rebuilt, and gfx_animation's ticker width cache has
always watched it. The menu drivers just never did.

font_driver_sync_impl() recomputes all five from font->metrics and two
width queries, but only when the generation has moved - otherwise it
is an integer compare and a return. font_flush() calls it, so the
drivers that go through it pick up a rebuild before anything is drawn
with the stale numbers.

Recomputing wideglyph_width needs the string it was measured from, so
font_data_impl_t keeps it. ozone and materialui set it where they
already had it to hand.

Two callers are not covered and are not made to look as though they
are: xmb holds raw font_data_t pointers and caches its metrics
separately, and gfx_widgets has its own font struct with its own
copies. Both call renderer->flush directly rather than font_flush(),
so neither reaches this hook. They need the same treatment before the
language switch can rely on it everywhere.

samples/gfx/font_lifecycle gains a case for this: that a first sync
computes the values, that a second with nothing changed does not touch
them, and that a sync after a rebuild recomputes them. Clean under
ASan, UBSan, LeakSanitizer and TSan.
This commit is contained in:
Y authored and libretroadmin committed 2026-08-08 18:54:57 +02:00
1 parent 76a9aeeb1f
commit 7eddfc2058
5 files changed
+87

No files matched your search

+41
View File
@@ -688,12 +688,53 @@ void font_driver_bind_block(void *font_data, void *block)
}
/* Flushing is slow - only do it if font has actually been used */
void font_driver_sync_impl(font_data_impl_t *font_data)
{
int glyph_width;
uint32_t gen = font_driver_get_generation();
if (!font_data || !font_data->font)
return;
if (font_data->metrics_generation == gen)
return;
font_data->metrics_generation = gen;
if ((glyph_width = font_driver_get_message_width(
font_data->font, "a", 1, 1.0f)) > 0)
font_data->glyph_width = (unsigned)glyph_width;
font_data->wideglyph_width = 100;
if (font_data->wideglyph_str && glyph_width > 0)
{
int wide = font_driver_get_message_width(font_data->font,
font_data->wideglyph_str,
strlen(font_data->wideglyph_str), 1.0f);
if (wide > 0)
font_data->wideglyph_width = wide * 100 / glyph_width;
}
font_data->line_height =
(int)roundf(font_data->font->metrics.height);
font_data->line_ascender =
(int)roundf(font_data->font->metrics.ascender);
font_data->line_centre_offset =
(int)roundf((font_data->font->metrics.ascender
- font_data->font->metrics.descender) * 0.5f);
}
void font_flush(
unsigned video_width,
unsigned video_height,
font_data_impl_t *font_data)
{
const font_renderer_t *renderer = font_data->font ? font_data->font->renderer : NULL;
/* A rebuilt font has different metrics; pick them up before
* anything is drawn with the old ones. */
font_driver_sync_impl(font_data);
if (font_data->raster_block.carr.coords.vertices == 0)
return;
if (renderer && renderer->flush)
+12
View File
@@ -112,6 +112,13 @@ typedef struct
int line_height;
int line_ascender;
int line_centre_offset;
/* The string wideglyph_width is measured from, kept so the derived
* values above can be recomputed without the driver's help. */
const char *wideglyph_str;
/* font_driver generation these were computed at. When the font is
* rebuilt underneath - switching menu language picks a different
* face - the generation moves and they are recomputed. */
uint32_t metrics_generation;
} font_data_impl_t;
void font_driver_bind_block(void *font_data, void *block);
@@ -148,6 +155,11 @@ unsigned font_driver_reload_fonts(void);
* cached per-font derived data */
uint32_t font_driver_get_generation(void);
/* Recompute the derived metrics above if the font has been rebuilt
* since they were last worked out. Cheap when nothing has changed:
* one integer compare. */
void font_driver_sync_impl(font_data_impl_t *font_data);
void font_flush(
unsigned video_width,
unsigned video_height,
+4
View File
@@ -9369,6 +9369,10 @@ static void materialui_init_font(gfx_display_t *p_disp,
font_data->glyph_width = (unsigned)char_width;
font_data->wideglyph_width = 100;
/* Kept so the derived metrics can be recomputed if the font is
* rebuilt - a language switch picks a different face. */
font_data->wideglyph_str = wideglyph_str;
font_data->metrics_generation = font_driver_get_generation();
if (wideglyph_str)
{
+4
View File
@@ -9935,6 +9935,10 @@ static bool ozone_init_font(
font_data->glyph_width = glyph_width;
font_data->wideglyph_width = 100;
/* Kept so the derived metrics can be recomputed if the font is
* rebuilt - a language switch picks a different face. */
font_data->wideglyph_str = wideglyph_str;
font_data->metrics_generation = font_driver_get_generation();
if (wideglyph_str)
{
@@ -144,6 +144,32 @@ int main(void)
}
}
/* 7. derived metrics on an impl follow a rebuild */
{
font_data_impl_t impl;
memset(&impl, 0, sizeof(impl));
impl.font = a;
impl.wideglyph_str = "WW";
font_driver_sync_impl(&impl);
{
int h0 = impl.line_height;
unsigned g0w = impl.glyph_width;
CHECK(h0 > 0, "impl metrics computed on first sync");
CHECK(g0w > 0, "impl glyph width computed");
/* nothing changed: sync must be a no-op */
impl.line_height = -1;
font_driver_sync_impl(&impl);
CHECK(impl.line_height == -1,
"sync does nothing when the generation is unchanged");
/* rebuild: the sync must notice and recompute */
font_driver_reload_fonts();
font_driver_sync_impl(&impl);
CHECK(impl.line_height == h0,
"impl metrics recomputed after a rebuild");
CHECK(impl.wideglyph_width > 0, "wideglyph width recomputed");
}
}
font_driver_free(a);
font_driver_free(c);
CHECK(live_renderer_state == 0, "all renderer state released at teardown");