帧率抖动定义与影响
都说掉帧,但帧率抖动到底是个什么东西?我见过不少团队把偶尔一次卡顿叫抖动,又把持续低帧也叫抖动,其实这两码事。说白了,帧率抖动是指帧率在时间轴上的不规则波动——不是帧率低,而是帧率忽高忽低。
1. 到底什么是帧率抖动?
拿一个实际场景举例:你的应用跑在 60fps,理想情况下每帧耗时 16.6ms。突然有一帧花了 80ms,然后下一帧又回到 16ms,接着再来一帧 50ms……这种非周期性的延迟变化,就是抖动。
你想想看,它跟「帧率低」有本质区别:
- 帧率低(比如稳定 30fps):每一帧都花 33ms,画面节奏均匀,用户适应后其实不太难受。
- 帧率抖动(比如 60fps 里突然冒出两个 80ms 的帧):画面突然顿一下,节奏被打断,用户立刻觉得“卡”。
我在项目中遇到过这种情况:用 dumpsys gfxinfo 看平均帧率 55fps 很漂亮,但用 Systrace 一看,后台调度每隔 1~2 秒就触发一次高频唤醒,把渲染线程抢走了。那几帧的耗时直接飙到 90ms 以上——这就是典型的后台调度造成的帧率抖动。
2. 度量指标之一:Jank(卡顿计数)
怎么量化抖动?最直观的指标就是 Jank。它统计的是「超时帧」的数量。
怎么算?我们定一个阈值(通常取目标帧率的 2 倍耗时)。比如目标是 60fps,阈值就是 33ms(2 帧的耗时)。任何一帧耗时超过 33ms,就算一次 Jank。
| 目标帧率 | Jank 阈值 | 含义 |
|---|---|---|
| 30fps | 66ms | 超过 2 帧未更新的帧 |
| 60fps | 33ms | 超过 2 帧未更新的帧 |
| 120fps | 16.6ms | 超过 2 帧未更新的帧 |
Jank 的优点是直观:你看到数字就能知道「这轮测试里一共卡了多少次」。但它有个致命缺陷——它只看单帧是否超标,不看超标幅度。
// 伪代码示意:Jank 计数逻辑
int jankCount = 0;
for (long frameTime : frameTimes) {
if (frameTime > JANK_THRESHOLD_MS) {
jankCount++;
}
}
// 输出:Jank = 23 次,但不知道每帧到底卡了多少毫秒
我曾经犯过一个错:只看 Jank 数量就觉得性能好。结果用户反馈“一卡一卡的”,最后发现虽然 Jank 数不多(才 3 次),但每次卡顿都长达 150ms——用户体验极差。所以 Jank 只能当敲门砖,不能做唯一判断。
3. 度量指标之二:FrameTime 方差
要弥补 Jank 的不足,我们必须看帧耗时的离散程度。说白了,就是这些帧的耗时到底有多散?标准差或方差是最合适的工具。
方差越大,帧之间的间隔越不均匀,抖动越严重。极端情况:假设所有帧都是 16.6ms,方差为 0,完美平滑。但如果有一半帧是 8ms,另一半是 25ms,方差就很大,虽然平均帧率可能依然是 60fps,但画面会明显一快一慢。
计算公式(标准方差):
σ = sqrt( Σ (frameTime_i - mean)^2 / N )
单位:ms。值越小越好。
我们团队内部设了一个经验阈值:对于 60fps 的目标,FrameTime 方差超过 3ms,用户就能感知到不流畅;超过 6ms,卡顿就非常明显了。你有没有发现,有些手机在系统更新后会变“卡”?其实很多时候帧率没变(还是 60fps),但方差从 2ms 跳到了 8ms——这就是典型的抖动恶化。
4. 帧率抖动对用户体验的负面影响
别小看抖动,它带来的影响比稳定低帧率要严重得多。我来讲几个实际感知:
- 视觉卡顿:人眼对节奏变化极其敏感。稳定 30fps 可以接受,但 60fps 里混进 3 帧 100ms 的延迟,大脑会立刻提示“画面断了”。
- 触控延迟:触控采样和渲染是联动的。如果渲染抖了一下,触摸响应也会跟着滞后——你滑动一下,页面过一会才动。我做短视频播放器优化时亲测过,抖动从 5ms 涨到 15ms 方差时,用户划屏翻页的误触率直接翻倍。
- 功耗恶化:抖动往往伴随着频率变化和线程调度扰动。后台调度一抢 CPU,渲染线程就得等,系统为了补帧会突然拉高频率,功耗曲线像过山车一样。你想想看,本来平稳的功耗,硬生生被抖出一个个尖峰,电池扛不住。
- 系统响应变慢:Android 的输入系统与 VSYNC 挂钩。抖动会导致输入事件排队,出现“按了按钮没反应”或“动画回弹卡住”。
我记得做一个地图应用时,后台定位同步导致帧率抖动,用户反馈“放大缩小地图时画面一抽一抽的”,开 Systrace 一查发现是 GPS 唤醒触发了 Binder 同步,把主线程卡了 40ms。这 40ms 放平均值里根本看不出来,但方差直接飙到 12ms。
一句话总结: 帧率抖动 = 帧耗时标准差超标 + 用户感知卡顿 + 后台调度元凶。
下面这张图帮你把整个知识体系串起来:
从上图可以看得很清楚:帧率抖动 ≠ 帧率低,它有自己的度量体系,而且对用户体验的伤害比单纯掉帧更隐蔽、更难受。尤其是我前面提到的后台调度场景——一个非必要的定位唤醒,可能就把整个系统节奏打乱了。下节课我们用 Systrace 把这个元凶抓出来。