[PATCH v2 06/35] video: truetype: Use pre-allocated buffer for glyph rendering
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 CONFIG_CONSOLE_TRUETYPE_GLYPH_BUF to enable a pre-allocated buffer in the driver's private data. The buffer starts at 4KB and grows via realloc() as needed. When rendering a glyph, use this buffer to avoid malloc/free for normal characters. 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 the number of glyphs rendered. Use 'font info' to view the count. Co-developed-by: Claude Opus 4 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- Changes in v2: - Rename the Kconfig to just enable the feature: always allocate cmd/font.c | 24 ++++++++++- doc/usage/cmd/font.rst | 22 +++++++++- drivers/video/Kconfig | 22 ++++++++++ drivers/video/console_truetype.c | 69 ++++++++++++++++++++++++++++--- include/asm-generic/global_data.h | 14 +++++++ test/cmd/font.c | 17 ++++++++ 6 files changed, 159 insertions(+), 9 deletions(-) diff --git a/cmd/font.c b/cmd/font.c index 384751e787a..79218779a2d 100644 --- a/cmd/font.c +++ b/cmd/font.c @@ -11,6 +11,16 @@ #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[]) +{ + printf("glyphs rendered: %u\n", gd->glyph_count); + + return 0; +} +#endif + static int do_font_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -75,12 +85,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..f7a4897667b 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,14 @@ font size This changes the font size only. With no argument it shows the current size. +font info +~~~~~~~~~ + +This shows glyph rendering statistics, specifically the number of glyphs +rendered since the video console was set up. + +This subcommand requires CONFIG_VIDEO_GLYPH_STATS=y. + Examples -------- @@ -52,7 +61,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 +70,23 @@ This shows an example of selecting a bitmap font Truetype is active:: cantoraone_regular => font sel 8x16 +This shows glyph rendering statistics:: + + => font info + glyphs rendered: 32705 + Configuration ------------- The command is only available if CONFIG_CONSOLE_TRUETYPE=y. +CONFIG_CONSOLE_TRUETYPE_GLYPH_BUF enables a pre-allocated buffer for glyph +rendering, avoiding malloc/free per character. The buffer starts at 4KB and +grows as needed via realloc(). + +CONFIG_VIDEO_GLYPH_STATS enables tracking of glyph-rendering statistics. + Return value ------------ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 446ce51fe27..0f99ba1845b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -247,6 +247,28 @@ config CONSOLE_TRUETYPE_MAX_METRICS font metrics which are expensive to regenerate each time the font size changes. +config CONSOLE_TRUETYPE_GLYPH_BUF + bool "TrueType glyph buffer to reduce malloc traffic" + depends on CONSOLE_TRUETYPE + default y + help + Enable a pre-allocated buffer for rendering glyph bitmaps. This + avoids malloc/free for each character rendered, reducing memory + fragmentation and improving performance. + + The buffer starts at 4KB and grows via realloc() as needed to + accommodate larger glyphs. + +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..6e65f55d598 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -180,6 +180,10 @@ 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. + * @glyph_buf_size: Current size of glyph_buf in bytes */ struct console_tt_priv { struct console_tt_metrics *cur_met; @@ -190,6 +194,8 @@ struct console_tt_priv { struct video_fontdata *cur_fontdata; int pos_start; int pos_count; + u8 *glyph_buf; + int glyph_buf_size; }; /** @@ -365,6 +371,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 +447,61 @@ 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 realloc() to grow the buffer as needed. + */ + use_buf = false; + if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_GLYPH_BUF) && + xpl_phase() >= PHASE_BOARD_R) { + int need_size = width * height; + + if (need_size > priv->glyph_buf_size) { + int new_size = SZ_4K; + + /* use the next power of 2 */ + while (new_size < need_size) + new_size <<= 1; + priv->glyph_buf = realloc(priv->glyph_buf, new_size); + if (priv->glyph_buf) + priv->glyph_buf_size = new_size; + } + if (priv->glyph_buf) { + data = priv->glyph_buf; + use_buf = true; + } + } + if (!use_buf) { + data = malloc(width * height); + if (!data) + return width_frac; + } + gd_inc_glyph_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 +589,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 +603,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..7155f400db1 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -365,6 +365,12 @@ struct global_data { */ ulong video_bottom; #endif +#if CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS) + /** + * @glyph_count: number of glyphs rendered + */ + uint glyph_count; +#endif #ifdef CONFIG_BOOTSTAGE /** * @bootstage: boot stage information @@ -637,6 +643,14 @@ 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_count() gd->glyph_count +#define gd_inc_glyph_count() gd->glyph_count++ +#else +#define gd_glyph_count() 0 +#define gd_inc_glyph_count() +#endif + /** * enum gd_flags - global data flags * diff --git a/test/cmd/font.c b/test/cmd/font.c index adfeebe920d..4991608e267 100644 --- a/test/cmd/font.c +++ b/test/cmd/font.c @@ -98,3 +98,20 @@ 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 count; + + if (!CONFIG_IS_ENABLED(VIDEO_GLYPH_STATS)) + return -EAGAIN; + + count = gd_glyph_count(); + ut_assertok(run_command("font info", 0)); + ut_assert_nextline("glyphs rendered: %u", count); + ut_assert_console_end(); + + return 0; +} +FONT_TEST(font_test_info, UTF_CONSOLE); -- 2.43.0
participants (1)
-
Simon Glass