帧率与刷新率:屏幕刷新率 vs 应用帧率
做嵌入式 GUI 开发,帧率和刷新率这两个概念,我见过太多人搞混了。说白了,屏幕刷新率是硬件的能力,应用帧率是软件的表现。它们俩不是一回事,但必须配合好。
屏幕刷新率是什么?
屏幕刷新率,就是显示器每秒重绘画面的次数。单位是 Hz。比如 60Hz 的屏幕,每秒刷新 60 次。这个值由硬件决定,你改不了。
我遇到过不少工程师,拿着 60Hz 的屏,非要把帧率跑到 120fps。结果呢?画面撕裂,功耗还高。你想想看,屏幕每秒只能显示 60 帧,你多出来的 60 帧去哪了?
| 刷新率 | 每帧时间 | 常见场景 |
|---|---|---|
| 30 Hz | 33.3 ms | 低端电子纸、工控屏 |
| 60 Hz | 16.7 ms | 主流嵌入式屏 |
| 120 Hz | 8.3 ms | 高端消费电子 |
应用帧率又是什么?
应用帧率,是 GUI 系统实际渲染画面的速度。单位是 fps。它取决于你的 CPU 算力、GPU 性能、渲染管线效率。
举个例子。你写了个动画,每 10ms 渲染一帧,那就是 100fps。但屏幕只有 60Hz。这时候就出问题了。
核心原则:应用帧率最好等于屏幕刷新率的整数倍。60Hz 的屏,跑 60fps 或 30fps 都行。跑 45fps 反而难受。
垂直同步(V-Sync)的影响
垂直同步,说白了就是让应用帧率跟屏幕刷新率对齐。GPU 渲染完一帧,等屏幕刷新信号来了再送显。
我早期做智能手表项目时,没开 V-Sync。结果呢?屏幕上半部分显示的是第 N 帧,下半部分已经是第 N+1 帧了。用户滑动列表时,画面像被撕开了一样。这就是撕裂(tearing)。
开了 V-Sync 后,撕裂没了。但新问题来了:如果渲染一帧超过 16.7ms(60Hz 下),GPU 就得等下一个刷新周期。帧率直接掉到 30fps。用户会觉得卡顿。
注意:V-Sync 不是万能的。它解决了撕裂,但引入了输入延迟。游戏里按一下按钮,画面要等一帧才反应。嵌入式触控交互也一样。
双缓冲与三缓冲机制
为什么需要缓冲?因为 GPU 在渲染时,屏幕同时在读取显存。如果只有一个缓冲区,两边抢着读写,画面就乱了。
双缓冲:两个缓冲区,一个 front buffer(显示用),一个 back buffer(渲染用)。渲染完交换指针。简单高效。
但双缓冲有个坑。如果渲染速度跟不上刷新率,front buffer 一直没更新,用户看到的就是上一帧。这就是卡顿感。
三缓冲:再加一个中间缓冲区。GPU 可以提前渲染下一帧,不用等屏幕刷新。渲染速度波动时,画面更平滑。
| 机制 | 内存占用 | 延迟 | 平滑度 |
|---|---|---|---|
| 单缓冲 | 低 | 低 | 差(撕裂严重) |
| 双缓冲 | 中 | 中 | 好(无撕裂) |
| 三缓冲 | 高 | 高 | 最好(抗波动) |
我记得有个车载仪表盘项目,用的双缓冲。但动画复杂,偶尔一帧渲染超时,仪表指针就跳一下。换成三缓冲后,指针平滑多了。代价是多了几百 KB 的显存开销。嵌入式设备里,这个取舍得算清楚。
我的建议:内存够用就上三缓冲。内存紧张就优化渲染管线,保证每帧在 16.7ms 内完成。双缓冲 + 稳定的帧率,效果也不差。
帧率与刷新率的协同工作流
下面这张图,是我自己总结的帧率与刷新率协同流程。每次做新项目,我都会对着它检查一遍。
实际项目中的避坑指南
我曾经在一个智能家居面板项目里,遇到过一个诡异的 bug。屏幕偶尔闪一下,但频率不固定。查了两天,最后发现是帧率在 58-62fps 之间波动,跟 60Hz 的刷新率不同步。每次不同步,V-Sync 就强制等一帧,画面就闪一下。
解决办法?把帧率锁定在 60fps,或者直接关掉 V-Sync 用三缓冲。我选了后者,因为三缓冲能吸收波动。
另一个坑是双缓冲时的内存对齐。有些 MCU 的 DMA 传输要求缓冲区地址对齐到 4 字节或 8 字节。不对齐的话,传输速度慢一半,帧率直接腰斩。我建议你在初始化时检查缓冲区地址,加个断言。
经验总结:
- 先确认屏幕刷新率,再定目标帧率
- 帧率波动比低帧率更可怕,优先用三缓冲
- V-Sync 不是必须的,低延迟场景可以关掉
- 缓冲区地址对齐,能省一半 DMA 时间
嗯,帧率和刷新率这块,说白了就是让软件和硬件步调一致。你跑得快,屏幕跟不上,没用。你跑得稳,屏幕才能给你好脸色。