4. 动画刷新原理:刷新率与垂直同步(V-Sync)、帧生成时间(Frame Time)、卡顿与撕裂成因分析

做驱动优化这么多年,我见过太多开发者把动画卡顿简单归咎于「性能不够」。其实很多时候,问题出在刷新机制本身。你想想看,就算你的 GPU 每秒能渲染 200 帧,如果显示器和系统配合不好,用户看到的依然是撕裂和抖动。

这一节,我们就把刷新率、垂直同步、帧生成时间这几个核心概念彻底讲透。我会结合我实际调试过的案例,帮你理解卡顿和撕裂到底是怎么来的。

4.1 刷新率(Refresh Rate)

刷新率,说白了就是显示器每秒钟重绘画面的次数。单位是 Hz。常见的 60Hz 显示器,每秒刷新 60 次,每次间隔约 16.67ms。

但这里有个坑:刷新率是显示器的硬件属性,它和 GPU 的渲染速度是两套独立的时钟。GPU 画完一帧,显示器不一定刚好准备好接收。这就引出了后续所有问题的根源——时序错配。

刷新率 每帧间隔 常见场景
60 Hz 16.67 ms 普通办公、早期手机
90 Hz 11.11 ms 中端手机、VR 入门
120 Hz 8.33 ms 旗舰手机、电竞显示器
144 Hz 6.94 ms 高端电竞、专业显示器
我的习惯:在驱动层适配时,我通常会先读取 EDID(Extended Display Identification Data)中的实际刷新率范围,而不是硬编码。有些显示器标称 120Hz,实际在特定分辨率下只能跑到 119.88Hz,差这 0.12Hz 积累久了就会丢帧。

4.2 垂直同步(V-Sync)

垂直同步,英文叫 V-Sync(Vertical Synchronization)。它的作用很简单:强制 GPU 在显示器完成一次完整刷新后,再提交下一帧。

为什么要这么做?因为如果没有 V-Sync,GPU 可能在显示器刚扫到屏幕中间时就把新帧数据写进了显存。结果就是:屏幕上半部分是上一帧,下半部分是下一帧——这就是我们常说的画面撕裂(Tearing)。

核心结论:

  • 开启 V-Sync → 消除撕裂,但可能引入输入延迟和卡顿
  • 关闭 V-Sync → 响应更快,但画面撕裂明显
  • 自适应 V-Sync(如 FreeSync / G-Sync)→ 动态匹配,兼顾两者

我记得有一次调试一个视频播放器的驱动,用户反馈全屏播放时画面偶尔闪一下。查了半天,发现是 V-Sync 在窗口模式和全屏模式之间切换时,驱动没有正确重置垂直同步状态。嗯,这种边界情况,文档里通常不会写。

4.3 帧生成时间(Frame Time)

很多人只看 FPS(每秒帧数),觉得 FPS 60 就流畅了。其实这是个误区。FPS 是平均值,而用户感知到的是每一帧的生成时间是否稳定。

举个例子:

  • 场景 A:每帧稳定 16ms,持续 60 帧 → FPS 62.5,体验流畅
  • 场景 B:大部分帧 8ms,但每隔 10 帧突然跳到一个 50ms 的帧 → FPS 也能到 60 左右,但用户会明显感觉到「卡了一下」

这就是帧生成时间(Frame Time)的意义。它比 FPS 更能反映真实体验。

我曾经踩过的坑:在优化一个 GPU 驱动时,我盯着 FPS 曲线看了两天,始终觉得「数据没问题,但就是卡」。后来换成 Frame Time 曲线,一眼就发现了问题——每 1.5 秒有一个 80ms 的尖峰。最后定位到是某个 DMA 传输在等待总线锁。如果只看 FPS,这个尖峰被平均掉了,根本发现不了。

4.4 卡顿与撕裂的成因分析

我们把前面几个概念串起来,看看卡顿和撕裂到底是怎么发生的。

4.4.1 撕裂(Tearing)

撕裂的成因其实就一句话:GPU 在显示器刷新过程中切换了帧缓冲区。

具体流程:

  1. 显示器从显存中的 Front Buffer 读取数据,逐行扫描
  2. GPU 渲染完成后,将新帧写入 Back Buffer
  3. 如果没有 V-Sync,GPU 直接交换 Front/Back Buffer 指针
  4. 此时显示器可能只扫描到一半,剩下的半屏就变成了新帧

结果就是一条明显的撕裂线,横跨屏幕。

4.4.2 卡顿(Stutter / Jank)

卡顿的成因更复杂,常见的有:

类型 成因 典型表现
渲染超时 GPU 计算量过大,一帧超过 16.67ms 帧率直接腰斩,从 60 掉到 30
V-Sync 等待 GPU 渲染完成,但显示器还没刷新完 帧生成时间出现 16.67ms 的整数倍跳变
驱动调度延迟 驱动层提交命令的时机不对 随机出现的微小卡顿,Frame Time 曲线有毛刺
CPU 瓶颈 CPU 来不及提交渲染命令 GPU 利用率不高,但帧率上不去
我的调试经验:遇到卡顿,我第一件事不是看 GPU 频率,而是抓一段 systrace 或者 Perfetto 数据,看 CPU 和 GPU 之间的 Pipeline 是否流畅。很多时候,卡顿的根因在驱动层的 Buffer 管理,而不是渲染本身。

4.5 核心知识体系:刷新与卡顿全景图

下面这张图,我把整个动画刷新的核心流程和问题点画了出来。你可以把它当作调试时的检查清单。

动画刷新与卡顿成因全景图 动画刷新与卡顿成因全景图 用户输入 / 动画指令 CPU 处理 / 提交渲染命令 GPU 渲染 → 写入 Back Buffer V-Sync 同步判断 关闭 V-Sync 开启 V-Sync 画面撕裂(Tearing) 等待 V-Sync 信号 Buffer 交换 → 显示器刷新 卡顿(Stutter / Jank)成因 • 渲染超时(Frame Time > 16.67ms) • V-Sync 等待导致帧间隔翻倍 驱动调度延迟 / CPU 瓶颈 也会导致 Frame Time 抖动

4.6 实际调试中的关键指标

在驱动开发和性能优化中,我建议你重点关注这几个指标:

  • Frame Time 的 P95 / P99 值:平均帧率是骗人的,看尾部延迟才真实
  • V-Sync 命中率:开启 V-Sync 后,有多少帧是在垂直消隐期内提交的
  • Buffer 交换次数:驱动层实际发生的 Flip 次数,和预期是否一致
  • GPU 空闲时间片:GPU 是否因为等待 V-Sync 而频繁进入空闲状态

一句话总结:

动画流畅的关键,不是把 FPS 堆到多高,而是让每一帧的生成时间稳定在刷新间隔以内,并且和显示器的刷新节奏对齐。V-Sync 是工具,不是银弹。理解它的工作原理,你才能知道什么时候该开,什么时候该关,什么时候该用自适应方案。