3. 卡顿根源分析:掉帧、Jank卡顿、垂直同步关闭后的撕裂、输入延迟
做双缓冲优化,说白了就是要搞清楚一个问题:画面到底卡在哪了?
我见过不少团队,一上来就堆各种优化手段,结果卡顿依旧。为什么?因为没找到根因。这一章,咱们就把四个最常见的卡顿元凶拎出来,一个一个解剖。
核心观点:掉帧、Jank、撕裂、输入延迟,这四兄弟经常结伴出现,但成因完全不同。对症下药的前提,是能准确区分它们。
3.1 掉帧(Frame Drop)—— 最直观的卡顿
掉帧,说白了就是本该在1/60秒内画完的一帧,没画完。显示器到了刷新点,发现缓冲区里还是上一帧的老图,于是只能再显示一遍。
我个人习惯把掉帧分成两类:
- 偶发性掉帧:突然来一个耗时操作,比如加载资源、GC触发。帧率曲线会突然掉下去一个尖峰。
- 持续性掉帧:渲染管线整体扛不住,比如场景面数太多、OverDraw严重。帧率整体偏低,曲线平稳但数值低。
我记得有一次排查一个AR应用,帧率在30fps和60fps之间反复横跳。一开始以为是渲染压力大,后来用Profile工具一抓,发现是后台线程的日志写入每隔几百毫秒就锁住主线程。你看,掉帧的根因往往不在渲染本身。
避坑指南:我曾经被帧率计数器骗过。有些工具统计的是「平均帧率」,偶发性掉帧被平均掉了,根本看不出来。一定要看帧时间(Frame Time)的逐帧曲线,超过16.67ms的尖峰才是真凶。
3.2 Jank卡顿 —— 比掉帧更隐蔽的敌人
掉帧是帧没画完,Jank是帧画完了但显示时机不对。你想想看,如果一帧花了20ms画完,下一帧只花了5ms,虽然平均帧率看起来还行,但人眼对帧间隔的抖动极其敏感。
Jank的典型表现:画面不是一直卡,而是一顿一顿的,像在打嗝。
| 指标 | 掉帧 | Jank |
|---|---|---|
| 帧时间 | 单帧超过16.67ms | 帧时间波动大,标准差高 |
| 人眼感受 | 画面突然停滞 | 画面不流畅,有微卡顿 |
| 常见原因 | 渲染超时、资源加载 | 调度抖动、VSync对齐问题 |
| 检测方式 | 看帧率曲线尖峰 | 看帧间隔直方图 |
嗯,这里要注意:Jank在60Hz显示器上尤其明显。因为每帧只有16.67ms的预算,一旦帧时间波动超过2-3ms,人眼就能感知到。我做过一个实验,把帧时间从16ms稳定到18ms,用户反馈「流畅」;但如果在16ms和20ms之间来回跳,用户就会说「卡」。稳定比快更重要。
3.3 垂直同步关闭后的撕裂(Tearing)
垂直同步(VSync)关闭后,GPU画完一帧就往缓冲区里写,不管显示器当前在扫哪一行。结果就是:一帧画面里,上半部分是第N帧,下半部分是第N+1帧。中间那条错位的线,就是撕裂。
为什么会这样?因为显示器的扫描是从上往下一行一行刷的。如果GPU在扫描中途切换了缓冲区,画面就劈叉了。
注意:撕裂不是「卡顿」,它是画面错位。有些游戏玩家为了降低输入延迟,宁可忍受撕裂也不开VSync。但做UI应用,撕裂是绝对不能接受的——文字和图标错位,用户体验直接归零。
我记得在做一个视频播放器的时候,用户反馈全屏播放时画面中间有一条「水波纹」。排查了半天,发现是关闭了VSync导致的撕裂。后来用双缓冲+VSync强制同步,问题解决。但代价是输入延迟增加了半帧。
3.4 输入延迟(Input Lag)—— 你动了,画面没动
输入延迟,就是从你按下鼠标/触摸屏幕,到画面产生响应之间的时间差。这个延迟由三部分组成:
- 输入采集延迟:硬件把物理操作转换成数字信号的时间。
- 处理延迟:应用逻辑处理输入、更新状态的时间。
- 渲染+显示延迟:GPU渲染、缓冲区交换、显示器刷新的时间。
双缓冲本身就会引入至少1帧的延迟(因为要等缓冲区交换)。如果再加上三重缓冲,延迟可能到2-3帧。对于60Hz显示器,1帧就是16.67ms,3帧就是50ms——人眼已经能明显感觉到「不跟手」了。
关键权衡:VSync开得越严格,撕裂越少,但输入延迟越大。关闭VSync,延迟降低,但撕裂出现。双缓冲方案就是在撕裂和延迟之间走钢丝。
3.5 四者关系全景图
下面这张图,是我自己总结的四个问题的关系。你看一眼就能明白它们之间的因果链。
3.6 如何快速定位是哪个问题?
在实际项目中,我一般按这个顺序排查:
- 先看撕裂:肉眼观察画面有没有错位线。有的话,VSync没开或者双缓冲没生效。
- 再看掉帧:用Profile工具抓帧时间,看有没有超过16.67ms的尖峰。有的话,找渲染瓶颈。
- 再看Jank:如果帧时间没超,但感觉卡,算帧间隔标准差。超过3ms,就是Jank。
- 最后看输入延迟:用高速摄像或者专用工具测。如果前面三个都没问题,但用户说「不跟手」,那就是输入延迟。
一个小技巧:我习惯在调试版本里加一个可视化叠加层,实时显示帧时间、帧间隔、VSync状态。这样在真机上跑一遍,哪个环节出问题一目了然。别等到用户反馈了才去查,那时候日志都凉了。