如何解决LVGL一直满帧运行的假象
小区域渲染场景下的帧率统计异常排查
现象描述
作者在 SOC_LVGL 这个仓库分支开发帧率测试示例。
但我发现,当测试的区域比较小的时候,LVGL
自带的性能监视器会出现 FPS
满帧运行的现象。
以下是 lv_conf.h
的一行代码:
#define LV_DISP_DEF_REFR_PERIOD 1
改代码定义了 LVGL
的刷新间隔 LV_DISP_DEF_REFR_PERIOD
,单位是ms。
这里设置为 1
,意味着 FPS
(也就是帧率)的上限是 1000
。
如果我设置成 16
,那么就是最多就 62
帧,这种情况下跑满,我是不会怀疑 LVGL
的。
可是好巧不巧,我就是设置了 1
,试图榨干它的性能,结果跑满了?离谱到我觉得不可能。
于是,作者开始排查问题,最终成功解决,破除了这个满帧运行的假象!
问题分析
问题最终追溯到 LVGL
自带的性能监视器 lv_refr.c
。
以下是 lv_refr.c
中问题所处的部分代码片段:
void _lv_disp_refr_timer(lv_timer_t * tmr) {
...
// 如果启用了性能监视器 LV_USE_PERF_MONITOR 和 LV_USE_LABEL
#if LV_USE_PERF_MONITOR && LV_USE_LABEL
// 用来显示数据的标签,就是在屏幕上看到的 xx FPS 和 xx% CPU
lv_obj_t * perf_label = perf_monitor.perf_label;
if(perf_label == NULL) {
perf_label = lv_label_create(lv_layer_sys());
...
perf_monitor.perf_label = perf_label;
}
// if 收集 300ms 的渲染数据,然后交给 else 进行帧率计算,最后回到 if
if(lv_tick_elaps(perf_monitor.perf_last_time) < 300) {
// 如果 300ms 内累计的渲染总像素数量 px_num 大于该数值,则统计一帧
// 否则就会忽略该帧
if(px_num > 5000) {
perf_monitor.elaps_sum += elaps;
perf_monitor.frame_cnt ++; // 程序初始化后 frame_cnt 为 0
}
}
else {
perf_monitor.perf_last_time = lv_tick_get();
// 计算帧率上限,这里的 period 就等于 LV_DISP_DEF_REFR_PERIOD
// 也就是我设置的 1,代入计算可得 fps_limit = 1000
uint32_t fps_limit = 1000 / disp_refr->refr_timer->period;
uint32_t fps;
if(perf_monitor.elaps_sum == 0) {
perf_monitor.elaps_sum = 1;
}
// 这里是问题的直接体现
// 如果 frame_cnt 一直为 0,那么帧率将一直等于上限也就是满帧运行
// 那为什么 frame_cnt 会一直为 0
// 因为当前设计的测试程序在 300ms 累计渲染的像素总数小于 5000
// 从而导致 LVGL 没有对这些渲染进行帧计数,所以 frame_cnt 会一直为 0
if(perf_monitor.frame_cnt == 0) {
fps = fps_limit;
}
else {
fps = (1000 * perf_monitor.frame_cnt) / perf_monitor.elaps_sum;
}
...
// 这里是标签的数据更新
lv_label_set_text_fmt(perf_label, "%"LV_PRIu32" FPS\n%"LV_PRIu32"%% CPU", fps, cpu);
}
#endif
...
}
如何解决
只需要修改源码,把原先的 5000
这个计数阈值改小即可。
为了让最小渲染区域得到计数,我改成了 1000
。
因为项目最小的测试区域是 32 * 32 = 1024
刚好大于 1000
。