Am 10. Dezember 2025 01:06:57 MEZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <simon.glass@canonical.com>
The TrueType console driver calls malloc/free for every character rendered, which causes significant memory fragmentation and allocation traffic.
Add a pre-allocated buffer (CONFIG_CONSOLE_TRUETYPE_GLYPH_BUF_SIZE, default 4KB) to the driver's private data. When rendering a glyph, use this buffer if the glyph bitmap fits, avoiding malloc/free for normal characters. Fall back to malloc only for oversized glyphs.
Please, avoid the config variable. Instead use realloc() to resize the buffer whenever it is too small. This should retain the performance gain but will allow for arbitrary font sizes set by the user. Best regards Heinrich
The buffer is allocated lazily after relocation to avoid consuming early malloc space before the full heap is available.
Add CONFIG_VIDEO_GLYPH_STATS (default y on sandbox) to track cumulative glyph rendering statistics in global_data. This persists across video device rebinds and can be viewed via 'font info'.
Co-developed-by: Claude Opus 4 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> ---
cmd/font.c | 30 ++++++++++++++-- doc/usage/cmd/font.rst | 20 ++++++++++- drivers/video/Kconfig | 24 +++++++++++++ drivers/video/console_truetype.c | 59 +++++++++++++++++++++++++++---- include/asm-generic/global_data.h | 22 ++++++++++++ test/cmd/font.c | 20 +++++++++++ 6 files changed, 166 insertions(+), 9 deletions(-)
diff --git a/cmd/font.c b/cmd/font.c index 384751e787a..99c8ae76f2e 100644 --- a/cmd/font.c +++ b/cmd/font.c @@ -11,6 +11,22 @@ #include <video.h> #include <video_console.h>
+#if CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS) +static int do_font_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int buf_count, malloc_count; + + /* read the results first so that they don't change while printing */ + buf_count = gd->glyph_buf_count; + malloc_count = gd->glyph_malloc_count; + printf("glyph buffer renders: %u\n", buf_count); + printf("glyph malloc renders: %u\n", malloc_count); + + return 0; +} +#endif + static int do_font_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -75,12 +91,22 @@ static int do_font_size(struct cmd_tbl *cmdtp, int flag, int argc, return 0; }
+#if CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS) +#define FONT_INFO_HELP "\nfont info - show glyph rendering statistics" +#define FONT_INFO_SUB , U_BOOT_SUBCMD_MKENT(info, 1, 1, do_font_info) +#else +#define FONT_INFO_HELP +#define FONT_INFO_SUB +#endif + U_BOOT_LONGHELP(font, "list - list available fonts\n" "font select <name> [<size>] - select font to use\n" - "font size <size> - select font size to"); + "font size <size> - select font size to" + FONT_INFO_HELP);
U_BOOT_CMD_WITH_SUBCMDS(font, "Fonts", font_help_text, U_BOOT_SUBCMD_MKENT(list, 1, 1, do_font_list), U_BOOT_SUBCMD_MKENT(select, 3, 1, do_font_select), - U_BOOT_SUBCMD_MKENT(size, 2, 1, do_font_size)); + U_BOOT_SUBCMD_MKENT(size, 2, 1, do_font_size) + FONT_INFO_SUB); diff --git a/doc/usage/cmd/font.rst b/doc/usage/cmd/font.rst index 6e313e70c7a..5e1f6e446e4 100644 --- a/doc/usage/cmd/font.rst +++ b/doc/usage/cmd/font.rst @@ -14,6 +14,7 @@ Synopsis font list font select [<name> [<size>]] font size [<size>] + font info
Description ----------- @@ -38,6 +39,15 @@ font size
This changes the font size only. With no argument it shows the current size.
+font info +~~~~~~~~~ + +This shows glyph rendering statistics. The TrueType driver uses a pre-allocated +buffer to avoid malloc/free for each character rendered. This command shows how +many glyphs were rendered using the buffer vs. falling back to malloc. + +This subcommand requires CONFIG_VIDEO_GLYPH_STATS=y. + Examples --------
@@ -52,7 +62,7 @@ Examples => font select cantoraone_regular 20 =>
-This shows an example of selecting a bitmap font Truetype is active:: +This shows an example of selecting a bitmap font when Truetype is active::
=> font list 8x16 @@ -61,12 +71,20 @@ This shows an example of selecting a bitmap font Truetype is active:: cantoraone_regular => font sel 8x16
+This shows glyph rendering statistics:: + + => font info + glyph buffer renders: 32705 + glyph malloc renders: 0 +
Configuration -------------
The command is only available if CONFIG_CONSOLE_TRUETYPE=y.
+CONFIG_VIDEO_GLYPH_STATS enables tracking of glyph-rendering statistics. + Return value ------------
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 446ce51fe27..c09341a08dc 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -247,6 +247,30 @@ config CONSOLE_TRUETYPE_MAX_METRICS font metrics which are expensive to regenerate each time the font size changes.
+config CONSOLE_TRUETYPE_GLYPH_BUF_SIZE + int "TrueType per-character glyph buffer size" + depends on CONSOLE_TRUETYPE + default 4096 + help + This sets the size of a pre-allocated buffer for rendering glyph + bitmaps. If a glyph fits within this buffer, no malloc/free is + needed during character rendering, which reduces memory fragmentation + and improves performance. Glyphs larger than this size fall back to + malloc. + + The default of 4096 bytes supports characters up to 64x64 pixels, + which covers most font sizes. Increase this if using very large fonts. + +config VIDEO_GLYPH_STATS + bool "Track glyph rendering statistics" + depends on CONSOLE_TRUETYPE + default y if SANDBOX + help + Track cumulative glyph rendering statistics in global_data, so they + persist across video device rebinds. This allows seeing the total + count of glyphs rendered using the pre-allocated buffer vs. malloc + fallback. Use 'font info' to view the statistics. + config SYS_WHITE_ON_BLACK bool "Display console as white on a black background" default y if ARCH_AT91 || ARCH_EXYNOS || ARCH_ROCKCHIP || ARCH_TEGRA || X86 || ARCH_SUNXI diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 7f5a2262b17..f335c9f1148 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -180,6 +180,9 @@ struct console_tt_metrics { * @pos_start: Value of pos_ptr when the cursor is at the start of the text * being entered by the user * @pos_count: Maximum value reached by pos_ptr (initially zero) + * @glyph_buf: Pre-allocated buffer for rendering glyphs. If a glyph fits, + * this avoids malloc/free per character. Allocated lazily after + * relocation to avoid using early malloc space. */ struct console_tt_priv { struct console_tt_metrics *cur_met; @@ -190,6 +193,7 @@ struct console_tt_priv { struct video_fontdata *cur_fontdata; int pos_start; int pos_count; + u8 *glyph_buf; };
/** @@ -365,6 +369,7 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, int advance; void *start, *end, *line; int row, kern; + bool use_buf;
/* Use fixed font if selected */ if (priv->cur_fontdata) @@ -440,13 +445,53 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, * information into the render, which will return a 8-bit-per-pixel * image of the character. For empty characters, like ' ', data will * return NULL; + * + * Use the pre-allocated glyph buffer if large enough, falling back to + * malloc for oversized glyphs. This avoids alloc/free traffic for + * normal characters. */ - data = stbtt_GetCodepointBitmapSubpixel(font, met->scale, met->scale, - x_shift, 0, cp, &width, &height, - &xoff, &yoff); - if (!data) + { + int ix0, iy0, ix1, iy1; + + stbtt_GetCodepointBitmapBoxSubpixel(font, cp, met->scale, + met->scale, x_shift, 0, + &ix0, &iy0, &ix1, &iy1); + width = ix1 - ix0; + height = iy1 - iy0; + xoff = ix0; + yoff = iy0; + } + if (!width || !height) return width_frac;
+ /* + * Use the pre-allocated buffer if available and large enough. Allocate + * it lazily, but only after relocation to avoid using early malloc. + */ + use_buf = false; + if (xpl_phase() >= PHASE_BOARD_R && + width * height <= CONFIG_CONSOLE_TRUETYPE_GLYPH_BUF_SIZE) { + if (!priv->glyph_buf) { + priv->glyph_buf = + malloc(CONFIG_CONSOLE_TRUETYPE_GLYPH_BUF_SIZE); + } + if (priv->glyph_buf) { + data = priv->glyph_buf; + use_buf = true; + gd_inc_glyph_buf_count(); + } + } + if (!use_buf) { + data = malloc(width * height); + if (!data) + return width_frac; + gd_inc_glyph_malloc_count(); + } + + stbtt_MakeCodepointBitmapSubpixel(font, data, width, height, width, + met->scale, met->scale, x_shift, 0, + cp); + /* Figure out where to write the character in the frame buffer */ bits = data; start = vid_priv->fb + y * vid_priv->line_length + @@ -534,7 +579,8 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, break; } default: - free(data); + if (!use_buf) + free(data); return -ENOSYS; }
@@ -547,7 +593,8 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, width, height);
- free(data); + if (!use_buf) + free(data);
return width_frac; } diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index cff9066de53..d91103dfe00 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -365,6 +365,16 @@ struct global_data { */ ulong video_bottom; #endif +#if CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS) + /** + * @glyph_buf_count: glyphs rendered using pre-allocated buffer + */ + uint glyph_buf_count; + /** + * @glyph_malloc_count: glyphs rendered using malloc fallback + */ + uint glyph_malloc_count; +#endif #ifdef CONFIG_BOOTSTAGE /** * @bootstage: boot stage information @@ -637,6 +647,18 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #define gd_pager_page_len() 0 #endif
+#if CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS) +#define gd_glyph_buf_count() gd->glyph_buf_count +#define gd_inc_glyph_buf_count() gd->glyph_buf_count++ +#define gd_glyph_malloc_count() gd->glyph_malloc_count +#define gd_inc_glyph_malloc_count() gd->glyph_malloc_count++ +#else +#define gd_glyph_buf_count() 0 +#define gd_inc_glyph_buf_count() +#define gd_glyph_malloc_count() 0 +#define gd_inc_glyph_malloc_count() +#endif + /** * enum gd_flags - global data flags * diff --git a/test/cmd/font.c b/test/cmd/font.c index adfeebe920d..1bdc14e0250 100644 --- a/test/cmd/font.c +++ b/test/cmd/font.c @@ -98,3 +98,23 @@ static int font_test_base(struct unit_test_state *uts) } FONT_TEST(font_test_base, UTF_SCAN_PDATA | UTF_SCAN_FDT | UTF_CONSOLE | UTF_DM); + +/* Test 'font info' command */ +static int font_test_info(struct unit_test_state *uts) +{ + int buf_count, malloc_count; + + if (!CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS)) + return -EAGAIN; + + /* read the results first so that they don't change while printing */ + buf_count= gd_glyph_buf_count(); + malloc_count = gd_glyph_malloc_count(); + ut_assertok(run_command("font info", 0)); + ut_assert_nextline("glyph buffer renders: %u", buf_count); + ut_assert_nextline("glyph malloc renders: %u", malloc_count); + ut_assert_console_end(); + + return 0; +} +FONT_TEST(font_test_info, UTF_CONSOLE);