资源优化技巧¶
嵌入式 GUI 项目中,Flash 和 RAM 资源极其有限。本文介绍一系列实用的优化技巧,帮助在有限资源下实现最佳的 GUI 效果。
字体裁剪¶
字体资源通常是 Code Size 的主要消耗者之一。
只包含需要的字符¶
在 supported_text.txt 中只列出项目实际使用的字符,避免包含多余字符:
# 只包含界面上实际显示的文本
Hello World
Settings
OK Cancel
0123456789
选择合适的字体位深¶
fontbitsize |
效果 |
存储占用 |
|---|---|---|
1 |
无抗锯齿,锯齿明显 |
最小 |
2 |
4 级灰度,轻微抗锯齿 |
较小 |
4 |
16 级灰度,效果良好 |
适中(推荐) |
8 |
256 级灰度,最佳效果 |
最大 |
对于小字体(12px 以下),1-bit 或 2-bit 即可;中等字体(14-20px)推荐 4-bit;大字体(24px 以上)可考虑 8-bit。
控制字体大小种类¶
每增加一个字体大小,就需要额外的一套字形数据。尽量统一界面中的字体大小,减少字体种类:
{
"font": [
{
"file": "font.ttf",
"text": "all_text.txt",
"pixelsize": "16",
"fontbitsize": "4"
}
]
}
避免使用 "pixelsize": "all" 或 "fontbitsize": "all",这会生成大量不必要的资源。
图片压缩¶
图片资源往往是占用空间最大的部分。
选择合适的像素格式¶
存储占用对比(100x100 像素图片):
RGB32 (无 Alpha): 40,000 字节
RGB565 (无 Alpha): 20,000 字节
Gray8 (无 Alpha): 10,000 字节
优先使用 RGB565,仅在需要高色彩精度时使用 RGB32。
降低 Alpha 位深¶
Alpha 通道占用对比(100x100 像素图片):
Alpha 8-bit: 10,000 字节
Alpha 4-bit: 5,000 字节
Alpha 2-bit: 2,500 字节
Alpha 1-bit: 1,250 字节
Alpha 0: 0 字节
大多数 UI 图标使用 4-bit Alpha 即可获得良好的视觉效果。
缩放到实际显示尺寸¶
不要使用超过实际显示尺寸的图片。在 app_resource_config.json 中使用 dim 参数:
{
"file": "background.png",
"dim": "120,160",
"format": "rgb565",
"alpha": "0"
}
将大图片放到外部存储¶
对于大尺寸、低频访问的图片,使用外部资源:
{
"file": "splash_screen.png",
"external": "1",
"format": "rgb565",
"alpha": "0"
}
代码裁剪(条件编译)¶
EmbeddedGUI 通过 app_egui_config.h 中的宏开关控制功能模块的编译。
关闭不需要的功能¶
// 不需要触摸支持
#define EGUI_CONFIG_FUNCTION_SUPPORT_TOUCH 0
// 不需要按键支持
#define EGUI_CONFIG_FUNCTION_SUPPORT_KEY 0
// 不需要焦点系统
#define EGUI_CONFIG_FUNCTION_SUPPORT_FOCUS 0
// 不需要外部资源
#define EGUI_CONFIG_FUNCTION_EXTERNAL_RESOURCE 0
// 不需要 ResourceManager
#define EGUI_CONFIG_FUNCTION_RESOURCE_MANAGER 0
// 不需要软件旋转
#define EGUI_CONFIG_FUNCTION_SOFTWARE_ROTATION_ENABLE 0
// 关闭调试信息
#define EGUI_CONFIG_DEBUG_LOG_LEVEL EGUI_LOG_IMPL_LEVEL_NONE
#define EGUI_CONFIG_DEBUG_PFB_REFRESH 0
#define EGUI_CONFIG_DEBUG_DIRTY_REGION_REFRESH 0
#define EGUI_CONFIG_DEBUG_PERF_MONITOR_SHOW 0
#define EGUI_CONFIG_DEBUG_MEM_MONITOR_SHOW 0
控件级裁剪¶
项目的构建系统基于 build.mk 模块化设计。只在 build.mk 中包含实际使用的控件源文件,未引用的控件不会被编译。
PFB 尺寸优化¶
PFB(Partial Frame Buffer)大小直接影响 RAM 占用和渲染效率。
PFB 大小计算¶
PFB RAM = PFB_WIDTH x PFB_HEIGHT x COLOR_BYTES
RGB565 下的典型配置:
PFB 配置 |
RAM 占用 |
每帧 draw_area 次数(240x320 屏) |
|---|---|---|
30x40 |
2,400B |
64 次 |
60x40 |
4,800B |
32 次 |
120x40 |
9,600B |
16 次 |
240x40 |
19,200B |
8 次 |
240x320 |
153,600B |
1 次(全屏缓冲) |
选择策略¶
RAM < 4KB:使用最小 PFB(如 15x20),接受较多的 draw_area 调用
RAM 4-8KB:使用 30x40 的 PFB,平衡 RAM 和效率
RAM > 16KB:可以使用较大的 PFB 或全屏缓冲
PFB 宽高建议优先选成屏幕宽高的整数约数;如果不能整除,框架也会处理边界块。
双缓冲优化¶
如果平台支持 DMA 异步传输,启用双缓冲可以让 CPU 和 DMA 并行工作:
#define EGUI_CONFIG_PFB_BUFFER_COUNT 2
代价是额外一份 PFB 大小的 RAM,但可以显著提升帧率。
RAM 优化¶
Page Union 模式¶
对于多页面应用,不同页面的控件实例可以使用 union 共享内存。因为同一时刻只有一个页面处于活跃状态:
// 多个页面共享同一块内存
typedef union {
struct {
egui_view_label_t title;
egui_view_button_t btn_start;
egui_view_image_t logo;
} home_page;
struct {
egui_view_label_t setting_title;
egui_view_switch_t switch_wifi;
egui_view_switch_t switch_bt;
egui_view_slider_t slider_brightness;
} settings_page;
} page_views_t;
static page_views_t page_views;
这样,两个页面的控件共享同一块 RAM,总 RAM 占用等于最大页面的占用。
减少静态实例¶
使用参数宏(
EGUI_VIEW_XXX_PARAMS_INIT)初始化控件,参数存储在 Flash 而非 RAM避免不必要的全局变量
字符串常量使用
const修饰,确保存储在 Flash
栈空间优化¶
GUI 框架的栈使用量取决于控件嵌套深度。典型配置下 2-4KB 栈空间即可。避免在回调函数中分配大的局部数组。
RAM 缓存配置优化¶
EmbeddedGUI 提供了多个缓存机制来提升渲染性能,但这些缓存会占用 RAM。根据项目的 RAM 预算,可以选择性地禁用或调整这些缓存。
字体渲染缓存¶
Draw Prefix Cache(~2.6 KB BSS)¶
最大的 RAM 占用项,缓存字符串的字形布局元数据(每个字符的 x 坐标、bbox、advance、字形下标等)。
// 默认配置(禁用以节省 RAM)
#define EGUI_CONFIG_FONT_STD_DRAW_PREFIX_CACHE_MAX_GLYPHS 0
#define EGUI_CONFIG_FONT_STD_DRAW_PREFIX_CACHE_SLOTS 0
// RAM 占用:0 B
// 启用缓存(适合静态文字 UI)
#define EGUI_CONFIG_FONT_STD_DRAW_PREFIX_CACHE_MAX_GLYPHS 64
#define EGUI_CONFIG_FONT_STD_DRAW_PREFIX_CACHE_SLOTS 2
// RAM 占用:2 slots × 64 glyphs × ~20 B ≈ 2612 B BSS
适用场景:静态文字 UI(label、title 等),同一字符串在多帧重复绘制时跳过字符串扫描和字形查找。
使用建议:
默认关闭以节省 RAM
如果应用有大量静态文字(如仪表盘、状态栏),可以开启以提升性能
全帧刷新场景(如性能测试、动画)缓存命中率为 0,不应开启
Line Cache(~164 B heap)¶
缓存多行文本的行分割结果,避免每次 get_str_size 或绘制时重新扫描 \n。
2026-04-06 起,这颗微型缓存开关已内收到 src/font/egui_font_std.c,不再作为 public 配置宏暴露。当前 shipped 行为固定为关闭;历史 1 仅带来文本矩形路径 +3.8% ~ +5.7% 级波动,同时增加 text +488B,不再值得为它保留一颗公共宏。
适用场景:多行文本控件(如多行 label)。
使用建议:
当前 public 面没有单独开关;默认继续关闭
如果业务确认多行文本是长期热点,优先评估更上层的字体缓存策略,或按项目分支定制实现
仅单行文本或固定字符串的场景无需为此引入额外配置分叉
ASCII Lookup Cache(268 B heap)¶
为 ASCII (0~127) 字符预建直查表,将字形查找从 O(log n) 降至 O(1)。当前它随 std-font fast draw 一起启用,不再保留单独的 2 档。
// 默认配置:启用完整 std-font fast draw,包含 ASCII 直查表
#define EGUI_CONFIG_FUNCTION_FONT_STD_FAST_DRAW 1
// ASCII cache RAM 占用:268 B heap + allocator overhead;egui_core_t 理论上增加 4 B
// 关闭整个 std-font fast draw 组
#define EGUI_CONFIG_FUNCTION_FONT_STD_FAST_DRAW 0
适用场景:纯英文 UI 应用,频繁 ASCII 字符渲染。
性能提升:
纯英文 UI:整体渲染加速约 10-20%
中英混合 UI:整体渲染加速约 5-10%
纯中文 UI:整体渲染加速约 1-2%
使用建议:
默认保持
1,避免继续拆分基础 fast draw 和 ASCII 直查表只有在 ROM / RAM 极紧张且可接受文本路径明显回退时才切到
0中文 UI 或低 RAM 场景不再需要单独考虑 ASCII lookup 的第二个 value
ASCII Lookup Index 8-bit(~128 B heap)¶
将 ASCII lookup 索引从 uint16_t 降为 uint8_t(需配合 EGUI_CONFIG_FUNCTION_FONT_STD_FAST_DRAW=1)。
2026-04-06 起,这颗微型宽度开关已内收到 src/font/egui_font_std.c,不再作为 public 配置宏暴露。历史 1 的静态 size / perf / runtime / unit 与 0 完全等价,只剩理论上的 ~128 B heap 节省,因此当前不再单独提供配置入口。
适用场景:小 ASCII 子集字体(如只有 88/93 个字形)。
使用建议:
当前 public 面没有单独开关
只有在已经保留
EGUI_CONFIG_FUNCTION_FONT_STD_FAST_DRAW=1且明确需要继续压缩该 cache 时,才值得在项目分支里定制实现
图像解码缓存¶
RLE External Cache Window(~1 KB BSS)¶
RLE 外部资源解码时的 I/O 窗口缓存,缓存控制字节(操作码+长度字段)以减少 semihosting I/O 调用。
// 默认配置(适合常规场景)
#define EGUI_CONFIG_IMAGE_RLE_EXTERNAL_CACHE_WINDOW_SIZE 1024
// RAM 占用:~1024 B BSS
// 低 RAM 优化
#define EGUI_CONFIG_IMAGE_RLE_EXTERNAL_CACHE_WINDOW_SIZE 64
// 节省:~960 B BSS
// 说明:像素字面量行(如 240px × 2B = 480 B)超过窗口大小时自动走直接 load,正确性不受影响
适用场景:外部 RLE 压缩图像资源。
低 RAM 建议:64 字节窗口满足控制流缓存需求,同时节省 960 B。
其他小型缓存(< 100 字节)¶
以下缓存占用较小,可根据需要调整:
// Alpha Opaque Cache(~33 B BSS,2026-04-06 起已内收到实现内部)
// 当前 shipped 行为固定为 4;历史 0 路径只回收 ~33 B,但当前主线存在 8 帧真实像素差
2026-04-06 起,这颗 RGB565+alpha source-opaque cache 微型宏已内收到 src/image/egui_image_std.c。当前默认继续保持 4,因为历史 4 -> 0 只回收 text -156B, bss -40B,远低于当前 1KB public 宏门槛,而且 0 路径还会引入真实 render diff。
其中 Code Lookup Cache ASCII Compact 这颗 ~20 B 级别的字体微型宏也已在 2026-04-06 内收到实现内部:历史 1 仅回收 data -20B,却会增加 text +200B,因此不再保留 public 配置入口。
低 RAM 配置示例¶
参考 example/HelloPerformance/app_egui_config.h 的激进低 RAM 配置:
// 禁用所有字体缓存(节省 ~2.9 KB)
#define EGUI_CONFIG_FONT_STD_DRAW_PREFIX_CACHE_MAX_GLYPHS 0
#define EGUI_CONFIG_FONT_STD_DRAW_PREFIX_CACHE_SLOTS 0
#define EGUI_CONFIG_FUNCTION_FONT_STD_FAST_DRAW 0
// 缩小 RLE 窗口(节省 ~960 B)
#define EGUI_CONFIG_IMAGE_RLE_EXTERNAL_CACHE_WINDOW_SIZE 64
// `EGUI_CONFIG_IMAGE_STD_ALPHA_OPAQUE_CACHE_SLOTS` 已内收为实现私有常量;
// 历史 `0` 路径只节省 ~33 B,但当前主线存在 8 帧真实像素差,不再写进推荐 snippet。
总节省:约 3.7 KB RAM(BSS + heap)
代价:字体渲染性能下降(每帧重新扫描字符串、重新查找字形)。适合全帧刷新的性能测试场景,不适合常规 UI 应用。LINE_CACHE_ENABLE、ASCII_LOOKUP_INDEX_8BIT、CODE_LOOKUP_CACHE_ASCII_COMPACT 这些低收益字体微型项现已内化,不再写进当前推荐 snippet。
综合优化检查清单¶
[ ] 字体只包含实际使用的字符
[ ] 字体位深选择 4-bit 或更低
[ ] 统一字体大小种类(不超过 3 种)
[ ] 图片使用 RGB565 格式
[ ] Alpha 通道使用 4-bit
[ ] 图片缩放到实际显示尺寸
[ ] 大图片使用外部资源
[ ] 关闭不需要的功能模块
[ ] 关闭调试日志和调试显示
[ ] PFB 大小适配 RAM 预算
[ ] 多页面使用 union 共享内存
[ ] 运行
python scripts/size_analysis/main.py --case-set typical确认优化效果