Debug Monitor 行为说明¶
概述¶
EGUI_CONFIG_DEBUG_INFO_SHOW 已移除。
当前的屏幕调试监视信息拆成两个独立开关:
EGUI_CONFIG_DEBUG_PERF_MONITOR_SHOW:显示 Performance MonitorEGUI_CONFIG_DEBUG_MEM_MONITOR_SHOW:显示 Memory Monitor
两者都采用 direct overlay 方式直接绘制到当前刷新路径里,不再依赖 EGUI_CONFIG_CORE_SEPARATE_USER_ROOT_GROUP_ENABLE,也不会额外创建调试控件树。
显示行为¶
当前实现分散在 src/core/egui_core_debug_perf.c、src/core/egui_core_debug_mem.c 和相关 refresh 路径中,行为是:
egui_core_refresh_screen()每次进入时,都会先更新 monitor 统计值。如果 monitor 文本发生变化,就把“上一次 overlay 区域”和“当前 overlay 区域”的并集重新标脏。
真正进入绘制时,overlay 会像普通内容一样随当前 dirty region / PFB 一起绘制。
绘制完成后,记录本次 overlay 实际区域,供下一次文本变化时做 union 标脏。
对应代码入口:
monitor 更新:
egui_core_refresh_screen()overlay 绘制:
egui_debug_draw_overlay_for_current_pfb()overlay 标脏:
egui_debug_mark_overlay_dirty()overlay 区域提交:
egui_debug_commit_overlay_region()
这意味着:
自动刷新路径下,即使业务层没有新的 dirty,Performance Monitor 仍然会按 refresh timer 统计 FPS。
当 monitor 文本变化时,overlay 自己会触发一次局部刷新,因此空闲页最终会收敛到
EGUI_CONFIG_MAX_FPS对应的显示值。Memory Monitor 没有单独的限频逻辑,只要页面进入刷新流程,就会读取当前
current / peak。
配置项¶
开关¶
#define EGUI_CONFIG_DEBUG_PERF_MONITOR_SHOW 1
#define EGUI_CONFIG_DEBUG_MEM_MONITOR_SHOW 1
两个开关彼此独立,可以单独启用。
统计窗口¶
#define EGUI_CONFIG_DEBUG_MONITOR_REFR_PERIOD 300U
当前代码里,这个宏实际用于 Performance Monitor 的统计窗口长度,单位是毫秒。
默认
300 msFPS、CPU、render avg、flush avg都基于这段窗口内的累计值计算Memory Monitor 不依赖这个窗口,它是每次刷新直接读取当前值
字体¶
#define EGUI_CONFIG_DEBUG_MONITOR_FONT (&egui_res_font_montserrat_12_4)
当前两个 monitor 共用同一个字体宏。
默认字体是
&egui_res_font_montserrat_12_4建议在
app_egui_config.h或USER_CFLAGS里覆盖,不要直接改默认头文件传入值应为
egui_font_t兼容字体对象的地址如果通过
USER_CFLAGS覆盖,注意 Windows shell 下&需要做转义
例如:
#define EGUI_CONFIG_DEBUG_MONITOR_FONT (&egui_res_font_montserrat_10_4)
或:
#define EGUI_CONFIG_DEBUG_MONITOR_FONT (&egui_res_font_montserrat_14_4)
位置¶
Performance Monitor 和 Memory Monitor 各自有独立的位置参数:
#define EGUI_CONFIG_DEBUG_PERF_MONITOR_POS EGUI_ALIGN_BOTTOM_RIGHT
#define EGUI_CONFIG_DEBUG_PERF_MONITOR_OFFSET_X 0
#define EGUI_CONFIG_DEBUG_PERF_MONITOR_OFFSET_Y 0
#define EGUI_CONFIG_DEBUG_MEM_MONITOR_POS EGUI_ALIGN_BOTTOM_LEFT
#define EGUI_CONFIG_DEBUG_MEM_MONITOR_OFFSET_X 0
#define EGUI_CONFIG_DEBUG_MEM_MONITOR_OFFSET_Y 0
可用对齐值与普通对齐宏一致,例如:
EGUI_ALIGN_TOP_LEFTEGUI_ALIGN_TOP_MIDEGUI_ALIGN_TOP_RIGHTEGUI_ALIGN_BOTTOM_LEFTEGUI_ALIGN_BOTTOM_MIDEGUI_ALIGN_BOTTOM_RIGHTEGUI_ALIGN_CENTER
OFFSET_X / OFFSET_Y 会在对齐结果上继续做像素偏移。
Performance Monitor¶
显示格式¶
第一行:
xx FPS, xx% CPU
第二行:
xx ms (xx | xx)
含义与 LVGL sysmon 对齐为:
第一行:
FPS、总 CPU%第二行:总时间
(Render | Flush)
当前实现见 src/core/egui_core_debug_perf.c 里的 egui_debug_set_perf_text() 和相关统计函数。
计算规则¶
设统计窗口长度为 elapsed_ms:
FPS = round(refr_count * 1000 / elapsed_ms)FPS最终会 clamp 到EGUI_CONFIG_MAX_FPSCPU = round((total_render_time + total_flush_time) * 100 / elapsed_ms)render_avg = round(total_render_time / render_count)flush_avg = round(total_flush_time / render_count)第二行总时间
total_ms = render_avg + flush_avg
其中:
refr_count:窗口内进入egui_core_refresh_screen()的次数render_count:窗口内真正完成一次绘制并统计了 render/flush 耗时的次数total_render_time:窗口内累计 render 时间total_flush_time:窗口内累计 flush 等待时间
这也是为什么空闲页面在 auto refresh 打开时,会显示接近 EGUI_CONFIG_MAX_FPS 的数值,而不是“用户手点了几次就显示几帧”。
参数意义¶
FPS表示 refresh timer 驱动下,当前窗口内的实际刷新节奏。CPU表示当前窗口内 GUI 渲染路径占用的近似比例,也就是(render + flush) / elapsed。 它不是操作系统意义上的全系统 CPU 占用率。xx ms表示平均总耗时,等于render_avg + flush_avg。(render | flush)前者是平均绘制耗时,后者是平均 flush/等待显示完成耗时。
Memory Monitor¶
显示格式¶
x.x kB
x.x kB max
当前实现见 src/core/egui_core_debug_mem.c 里的 egui_debug_set_mem_text(),以及 src/core/egui_api.c 里的 egui_api_malloc() / egui_api_free()。
统计范围¶
Memory Monitor 只统计 EGUI 自身通过 egui_api_malloc() / egui_api_free() 管理的 payload 大小:
current当前仍然存活的 payload 总和peak运行到当前时刻为止,current的历史最大值
另外,EGUI 内部按帧使用的 heap scratch/cache 会在整帧绘制和 flush 完成后统一释放。 因此空闲时第一行通常会回落,而第二行继续保留历史峰值。 如果第一行长期不回落,才更接近“仍有常驻分配没有释放”的含义。
注意:
不代表系统总 heap 使用量
不代表外部 allocator 的总容量
不统计碎片率
不统计
malloc header自己占掉的额外字节
计算规则¶
当前实现会在每个分配块前放一个很小的 header,只记录 payload_size。
分配时:
current += payload_size
peak = max(peak, current)
释放时:
current -= payload_size
egui_api_get_mem_monitor() 当前只回填:
used_sizemax_used
其余字段保持 0。
编译开销¶
只有在:
#define EGUI_CONFIG_DEBUG_MEM_MONITOR_SHOW 1
时,egui_api_malloc() 才会编译进 header 包装和 current / peak 统计逻辑。
默认关闭时:
不会引入这层 header
不会做 peak 计算
egui_api_malloc()/egui_api_free()直接走平台分配接口
运行截图¶
整体效果¶
下面这张图同时打开了 perf monitor 和 mem monitor。左下角是 memory,右下角是 performance。

Performance Monitor 放大图¶
第一行显示 FPS, CPU,第二行显示 总时间 (Render | Flush)。

Memory Monitor 放大图¶
这里显示的是 EGUI 自身动态申请内存的 current / peak,不是系统总 SRAM。

截图来源¶
本文图片均为 2026-04-02 在 PC 端实际运行录制后截取,命令如下。
debug_monitor_overview.png¶
make clean
make -j APP=HelloSimple PORT=pc TARGET=doc_debug_hello_simple APP_OBJ_SUFFIX=HelloSimple_doc_debug COMPILE_DEBUG= COMPILE_OPT_LEVEL=-O0 USER_CFLAGS='-DEGUI_CONFIG_DEBUG_PERF_MONITOR_SHOW=1 -DEGUI_CONFIG_DEBUG_MEM_MONITOR_SHOW=1'
output\doc_debug_hello_simple.exe output\app_egui_resource_merge.bin --record runtime_check_output/HelloSimple_debug_monitor 2 5 --speed 1 --clock-scale 6 --snapshot-stable-cycles 1 --snapshot-max-wait-ms 1500 --headless
使用 runtime_check_output/HelloSimple_debug_monitor/frame_0009.png 作为原图。
debug_monitor_mem_zoom.png¶
make clean
make -j APP=HelloBasic APP_SUB=viewpage_cache PORT=pc TARGET=doc_debug_viewpage_cache APP_OBJ_SUFFIX=HelloBasic_viewpage_cache_doc_debug COMPILE_DEBUG= COMPILE_OPT_LEVEL=-O0 USER_CFLAGS='-DEGUI_CONFIG_DEBUG_MEM_MONITOR_SHOW=1 -DEGUI_CONFIG_DEBUG_MEM_MONITOR_POS=EGUI_ALIGN_TOP_LEFT -DEGUI_CONFIG_DEBUG_MEM_MONITOR_OFFSET_X=6 -DEGUI_CONFIG_DEBUG_MEM_MONITOR_OFFSET_Y=6'
output\doc_debug_viewpage_cache.exe output\app_egui_resource_merge.bin --record runtime_check_output/HelloBasic_viewpage_cache_debug_mem 2 8 --speed 1 --clock-scale 6 --snapshot-stable-cycles 1 --snapshot-max-wait-ms 1500 --headless
使用 runtime_check_output/HelloBasic_viewpage_cache_debug_mem/frame_0015.png 裁剪得到放大图。
与 LVGL 的一致和差异¶
一致点:
Performance Monitor 文案格式对齐为
FPS / CPU / total(render | flush)overlay 位置和偏移由独立宏控制
两个 monitor 可以独立开关
差异点:
EGUI 当前的
CPU是 GUI 刷新路径的窗口占比,不是 idle-hook 意义上的全系统 CPUEGUI 当前的 Memory Monitor 只统计
egui_api_malloc()路径下的current / peak,没有总容量和碎片率
因此,这份 monitor 的目标是“对齐 LVGL 的使用习惯和显示结构”,而不是机械复刻全部底层统计口径。