1. 课程导入

各位做嵌入式UI的同行,咱们今天聊聊动画性能最底层的那些事。说实话,我见过太多项目,UI效果图做得漂漂亮亮,一上真机就卡成PPT。问题出在哪?多半是对帧率、刷新机制这些基础概念没吃透。

我自己刚接触TouchGFX那会儿,也踩过类似的坑。总觉得不就是让画面动起来嘛,结果一深入才发现,这里面的门道比想象中多得多。好,咱们正式开始。

1.1 帧率(FPS)到底是什么?

帧率,全称Frames Per Second,每秒传输的帧数。说白了,就是一秒钟内屏幕能刷新多少张画面。

你想想看,电影是24FPS,电视是30FPS,咱们做嵌入式UI,目标通常是60FPS。为什么是60?因为人眼的视觉暂留效应,60FPS已经能提供非常流畅的视觉体验了。

关键认知:帧率不是越高越好。60FPS和120FPS在人眼感知上差异极小,但CPU/GPU的负载却翻倍。我个人的习惯是,先稳定60FPS,再考虑优化功耗。

在TouchGFX里,帧率计算其实很简单:

// 伪代码:帧率计算逻辑
uint32_t fps = 0;
uint32_t lastTick = HAL_GetTick();
uint32_t frameCount = 0;

void onFrame() {
    frameCount++;
    uint32_t now = HAL_GetTick();
    if (now - lastTick >= 1000) {
        fps = frameCount;
        frameCount = 0;
        lastTick = now;
        // 此时fps就是当前帧率
    }
}

嗯,这里要注意:TouchGFX自带的性能监测工具可以直接看到实时FPS,但我建议你在开发阶段自己加一个这样的计数器,方便定位问题。

1.2 刷新机制:TouchGFX怎么把画面画上去?

TouchGFX的刷新机制,我总结为三个字:增量刷新。

什么意思?它不是每一帧都把整个屏幕重画一遍,而是只画变化的部分。这跟PC上的游戏引擎完全不同——游戏引擎恨不得每帧都渲染整个场景,但嵌入式MCU的资源有限,全屏重绘太奢侈了。

具体流程是这样的:

  1. 脏矩形检测:TouchGFX会标记哪些区域发生了变化(比如按钮按下、文字更新)
  2. 局部渲染:只渲染这些脏矩形区域
  3. DMA传输:通过DMA把渲染好的数据送到显存
  4. 屏幕刷新:LCD控制器从显存读取数据,显示到屏幕上

我的经验:曾经有个项目,动画卡顿严重。我打开TouchGFX的调试工具一看,发现脏矩形几乎覆盖了整个屏幕——等于每次都在全屏重绘。后来优化了动画逻辑,把脏矩形缩小到原来的1/5,帧率直接从25FPS飙到55FPS。

所以,理解脏矩形机制,是优化动画性能的第一步。

1.3 VSync同步原理:为什么画面会撕裂?

VSync,垂直同步。这个名字听起来高大上,其实原理很简单。

咱们的LCD屏幕,是从上到下逐行扫描的。如果CPU在屏幕扫描到一半的时候,突然把显存里的数据换了,就会出现上半张是旧画面、下半张是新画面的情况——这就是画面撕裂。

VSync的作用,就是让CPU/GPU等到屏幕扫描完一整个画面(垂直消隐期)之后,再更新显存数据。

避坑指南:我曾经在一个项目里关闭了VSync,想着能提高帧率。结果帧率确实上去了,但画面撕裂严重,用户体验极差。后来老老实实把VSync打开,虽然帧率降了一点,但画面流畅度反而提升了。

在TouchGFX里,VSync的配置通常在HAL层:

// TouchGFX HAL配置示例
static TouchGFXHAL hal;
hal.setFrameBufferStartAddress((uint16_t*)FRAME_BUFFER_ADDR);
hal.setVsyncInterval(16); // 60FPS对应的VSync间隔,单位ms
hal.setDisplayRefreshMode(touchgfx::HAL::DISPLAY_REFRESH_MODE_VSYNC);

这里有个细节:setVsyncInterval(16) 对应的是60FPS。如果你的屏幕是30FPS,那就设成33。这个值必须和屏幕的刷新率匹配,否则动画时序会乱。

1.4 三者之间的关系

帧率、刷新机制、VSync,这三者不是孤立的。我画了一张图,帮你理清它们的关系:

TouchGFX动画核心机制关系图 帧率 (FPS) 每秒画面更新次数 刷新机制 增量渲染 + 脏矩形 VSync同步 垂直消隐期更新显存 影响因素 • CPU负载 • 渲染复杂度 • 显存带宽 脏矩形策略 • 只更新变化区域 • 合并相邻脏矩形 • 避免全屏重绘 同步效果 • 消除画面撕裂 • 稳定帧率输出 • 降低功耗 三者协同:FPS决定流畅度,刷新机制决定效率,VSync保证画面完整性 优化目标:在VSync约束下,用最小脏矩形达到目标FPS

从这张图你能看出来,三个环节是环环相扣的。帧率上不去,可能是刷新机制没用好;画面撕裂,多半是VSync没配好。我建议你在做性能优化时,按这个顺序排查:先看VSync配置对不对,再看脏矩形是否合理,最后才去调帧率目标。

1.5 实际项目中的常见问题

我把这些年遇到的高频问题整理了一下,列个表方便你对照:

现象 可能原因 排查方向
帧率忽高忽低 VSync间隔不匹配 检查 setVsyncInterval 是否与屏幕刷新率一致
画面撕裂 VSync未开启或配置错误 确认 DISPLAY_REFRESH_MODE_VSYNC 已启用
动画卡顿 脏矩形过大或过多 使用TouchGFX Designer的Profiler查看脏矩形分布
帧率低于目标 渲染耗时超过VSync间隔 优化渲染逻辑,或降低目标帧率

一个小技巧:如果你不确定当前帧率是否稳定,可以在屏幕上放一个FPS显示控件。TouchGFX Designer里自带这个组件,拖出来就能用。我习惯在开发阶段一直开着它,就像开车看仪表盘一样。

好了,这一章的内容就到这里。帧率、刷新机制、VSync,这三个概念是TouchGFX动画性能优化的基石。后面的章节,我们会基于这些基础,一步步深入具体的优化技巧。