8、Vsync与帧同步:硬件Vsync与软件Vsync、Vsync偏移计算、Choreographer vs SurfaceFlinger vsync关系

说到Android的流畅度,Vsync是个绕不开的话题。

我刚开始接触图形系统时,一直以为Vsync就是个简单的垂直同步信号。后来踩了不少坑才明白——这里面的门道,远比想象中深。硬件Vsync、软件Vsync、偏移计算,再加上Choreographer和SurfaceFlinger之间的协作关系,任何一个环节出问题,你的App就会掉帧、卡顿。

今天我们就来彻底搞懂它。

8.1 硬件Vsync与软件Vsync

先说说最基础的概念。

硬件Vsync,说白了就是屏幕硬件本身发出的一个同步信号。每次屏幕刷新完一帧,硬件就会产生一个脉冲。这个脉冲告诉系统:「我准备好了,你可以送下一帧过来了。」

但问题来了——硬件Vsync的频率是固定的。比如60Hz的屏幕,每16.6ms来一次。可你的App不一定能在这个时间窗口内完成渲染。有时候快,有时候慢。硬件Vsync不会等你,它只管按时发信号。

核心区别:

  • 硬件Vsync:由屏幕驱动产生,频率固定,精度高
  • 软件Vsync:由系统通过软件模拟或分发的Vsync事件,用于协调各模块

软件Vsync是怎么来的?

嗯,这里要注意。Android系统在收到硬件Vsync后,并不会直接把它丢给App。而是由SurfaceFlinger这个「大管家」先接收,然后通过一系列分发机制,生成多个软件Vsync事件,分别送给Choreographer、Input系统、App渲染线程等。

我遇到过一个问题:某款手机在低电量模式下,软件Vsync的抖动特别大。后来排查发现,是系统为了省电,把硬件Vsync的采样周期拉长了,导致软件Vsync的精度下降。说白了,硬件是源头,软件是加工后的产物。

我的经验:

调试卡顿时,第一步就是确认硬件Vsync是否稳定。用systrace抓一下,看看Vsync事件的时间间隔是否均匀。如果硬件本身就不稳,后面再怎么优化App都没用。

8.2 Vsync偏移计算

Vsync偏移,这个概念很多人容易忽略。

你想想看,硬件Vsync信号从屏幕驱动传到SurfaceFlinger,再传到App,中间是有延迟的。这个延迟虽然只有几微秒到几十微秒,但在高刷新率屏幕上(比如120Hz),一个Vsync周期只有8.3ms。这点延迟就可能造成帧错位。

偏移计算就是为了解决这个问题。

具体怎么算?

Android系统会维护一个Vsync时间戳的偏移量。这个偏移量包括两部分:

  1. 硬件传输延迟:从屏幕驱动到SurfaceFlinger的物理延迟
  2. 软件处理延迟:SurfaceFlinger内部处理Vsync事件的时间

系统通过一个叫VsyncPredictor的组件来动态计算这个偏移。它会记录历史Vsync到达时间,然后用线性回归或卡尔曼滤波来预测下一个Vsync的精确时间。

// 伪代码:Vsync偏移计算逻辑
long predictedVsyncTime = lastVsyncTime + vsyncPeriod;
long offset = predictedVsyncTime - hardwareVsyncArrivalTime;
// 如果offset过大,说明预测不准,需要调整

我曾经在调试一款90Hz屏幕的手机时发现,它的Vsync偏移量在亮屏前几分钟内波动很大。原因是屏幕刚点亮时,驱动层的时钟还没稳定。嗯,这个坑让我折腾了两天。

避坑指南:

我曾经在自定义动画框架时,直接用了硬件Vsync的时间戳来做帧同步。结果在部分设备上动画总是抖动。后来才发现,应该用经过偏移校正后的软件Vsync时间戳。硬件时间戳虽然精确,但不一定和App的渲染管线对齐。

8.3 Choreographer vs SurfaceFlinger vsync关系

这两个东西的关系,是理解Android帧同步的关键。

SurfaceFlinger是系统的合成服务。它接收硬件Vsync,然后做两件事:

  • 触发合成:把各个App的Buffer合成到屏幕上
  • 分发Vsync:生成软件Vsync事件,分发给所有注册的监听者

Choreographer是App端的Vsync消费者。它从SurfaceFlinger那里拿到Vsync事件,然后通知App开始做三件事:

  • 输入处理(Input)
  • 动画计算(Animation)
  • 视图绘制(Traversal)

说白了,SurfaceFlinger是「发令员」,Choreographer是「运动员」。发令员一开枪,运动员才开始跑。

但这里有个关键点:Choreographer收到的Vsync,并不是SurfaceFlinger收到的那个原始Vsync。中间经过了偏移计算和分发延迟。所以Choreographer的Vsync回调时间,会比硬件Vsync晚那么一点点。

关系总结:

组件 角色 Vsync来源 主要工作
硬件驱动 生产者 屏幕硬件 产生原始Vsync脉冲
SurfaceFlinger 分发者 硬件Vsync 合成、分发软件Vsync
Choreographer 消费者 软件Vsync 触发App渲染流水线

我见过不少开发者直接在Choreographer的回调里做耗时操作,结果导致下一帧的Vsync回调被延迟。这就是典型的「自己拖死自己」。Choreographer的回调必须轻量,任何超过Vsync周期的工作都应该放到子线程。

调试技巧:

用adb命令可以查看当前系统的Vsync信息:

adb shell dumpsys window displays
adb shell dumpsys SurfaceFlinger --vsync

重点关注vsyncPeriodvsyncOffset这两个值。如果它们和屏幕标称的刷新率对不上,说明系统层面就有问题。

8.4 知识体系总览

为了让你更直观地理解整个Vsync链路,我画了一张图:

Vsync 链路与帧同步架构 屏幕硬件 60Hz / 90Hz / 120Hz 硬件Vsync SurfaceFlinger 接收硬件Vsync → 合成 → 分发软件Vsync 软件Vsync Choreographer Input / Animation / Traversal 合成指令 HWC合成 硬件合成器 送显 Vsync偏移计算 动态预测 + 校准 硬件层 系统服务层 App层 合成层

从这张图可以清楚看到:硬件Vsync是源头,SurfaceFlinger是中枢,Choreographer是执行者。偏移计算贯穿其中,确保每个环节的时间戳对齐。

最后说一句:理解Vsync,不能只看理论。我建议你拿一台手机,打开systrace,实际抓一下Vsync事件的时间分布。看看硬件Vsync和Choreographer回调之间到底差了多少。这个差距,就是你优化空间所在。

核心要点回顾:

  • 硬件Vsync是物理信号,软件Vsync是逻辑事件
  • Vsync偏移计算用于补偿传输和处理延迟
  • SurfaceFlinger负责分发,Choreographer负责消费
  • 任何一层的延迟都会导致帧率下降

公众号:蓝海资料掘金营,微信deep3321