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时间戳的偏移量。这个偏移量包括两部分:
- 硬件传输延迟:从屏幕驱动到SurfaceFlinger的物理延迟
- 软件处理延迟: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
重点关注vsyncPeriod和vsyncOffset这两个值。如果它们和屏幕标称的刷新率对不上,说明系统层面就有问题。
8.4 知识体系总览
为了让你更直观地理解整个Vsync链路,我画了一张图:
从这张图可以清楚看到:硬件Vsync是源头,SurfaceFlinger是中枢,Choreographer是执行者。偏移计算贯穿其中,确保每个环节的时间戳对齐。
最后说一句:理解Vsync,不能只看理论。我建议你拿一台手机,打开systrace,实际抓一下Vsync事件的时间分布。看看硬件Vsync和Choreographer回调之间到底差了多少。这个差距,就是你优化空间所在。
核心要点回顾:
- 硬件Vsync是物理信号,软件Vsync是逻辑事件
- Vsync偏移计算用于补偿传输和处理延迟
- SurfaceFlinger负责分发,Choreographer负责消费
- 任何一层的延迟都会导致帧率下降
公众号:蓝海资料掘金营,微信deep3321