video: truetype: Use pre-allocated buffer for glyph rendering

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.

Series-changes: 2
- Rename the Kconfig to just enable the feature: always allocate

Co-developed-by: Claude Opus 4 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-12-08 16:35:17 -07:00
parent 0987da845d
commit 69d2f4ab58
6 changed files with 159 additions and 9 deletions

View File

@@ -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);

View File

@@ -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
------------

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
*

View File

@@ -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);