关于VSync的产生,你需要彻底更新一个认知:Android系统里你看到的那些整齐的VSYNC-APP和VSYNC-SF信号,99%的时间里并不是硬件直接产生的,而是SurfaceFlinger用数学公式"算"出来的。
为了把这个"算出来"的过程讲透,我们从物理源头 、软件衍生 、按需开关三个层面逐步拆解。
一、物理源头:硬件VSync的"原始脉搏"
VSync这个概念的诞生比Android早几十年,它源自CRT显示器电子枪的物理动作。
1.1 它最初是显示器"喘口气"的信号
- 电子枪从左到右、从上到下扫描屏幕,扫完一帧后必须关闭电子束、从右下角瞬间移回左上角(垂直回扫)。
- 这段短暂的"空窗期"会触发一个脉冲------垂直同步信号(VSync),告诉显卡:"上一帧已显示完毕,可以送下一帧了"。
- 现代LCD虽无电子枪,但为了兼容,显示控制器(Display Controller)会模拟这个时序,通过硬件定时器产生固定频率的脉冲。这个频率就是屏幕刷新率(60Hz/90Hz/120Hz等)。
1.2 Android里谁在制造这个原始脉冲? Hardware Composer(HWC) 。它是显示驱动的一部分,直接与硬件定时器交互。每当屏幕完成一次刷新,HWC就通过回调向SurfaceFlinger发送一个HW_VSYNC_0信号。
关键认知 :这个硬件信号极其"原始"------它只是一串准确但没有任何业务逻辑的时间戳。它不知道谁是App、谁是SurfaceFlinger,只会每分钟60次(或更高)忠实地发出脉冲。
二、软件衍生:DispSync的"神算局"
如果让App和SurfaceFlinger每次都直接响应HW_VSYNC_0,会有两个严重问题:
- 功耗灾难:即使屏幕静止不动(如看电子书),App和SF也会每16.6ms被唤醒,疯狂空转。
- 相位死锁 :App绘制、SF合成、屏幕显示三者挤在同一个时间点开工,流水线严重阻塞(旧Android版本的痛点)。
于是Android 4.1(Project Butter)引入了DispSync模块 ------一个基于数学预测的软件VSync生成器。这是整个机制最精妙的部分。
2.1 DispSync的工作原理:锁相环的软件实现
DispSync本质上是一个数字锁相环(Digital Phase-Locked Loop) 。它不依赖硬件中断实时触发,而是通过采样硬件VSync的历史时间,拟合出一条精准的"虚拟时钟线"。
第一步:采样与建模
- SurfaceFlinger会持续接收HW_VSYNC_0信号,但并不立刻分发。
- DispSync收集至少3个、最多32个 硬件VSync时间戳,通过最小二乘法或均值滤波计算出平均周期(AvgPeriod)和平均相位(AvgPhase)。
- 数学公式(简化版):
- 将每个时间戳映射到单位圆角度:
ΔPhase = 2π * (timestamp % AvgPeriod) / AvgPeriod - 计算所有采样点的平均向量
(AvgX, AvgY) SW_VSYNC相位 = atan2(AvgY, AvgX)- 最终:
SW_VSYNC时间 = AvgPeriod + AvgPhase
- 将每个时间戳映射到单位圆角度:
一旦模型误差收敛(通常6个采样后),DispSync会立即关闭硬件VSync中断**,完全靠这套数学模型产生后续的软件VSync。这就是"用计算代替中断"的核心节能设计。
2.2 一源双生:VSYNC-APP与VSYNC-SF
硬件只提供一个原始脉冲,但系统需要两个不同用途的节拍器。DispSync在生成软件VSync时,会直接产出两条相位偏移的时钟流:
ini
VSYNC-APP = SW_VSYNC基准 + phase_app偏移量
VSYNC-SF = SW_VSYNC基准 + phase_sf偏移量
- phase_app:通常为0~2ms(应用先开始绘制)
- phase_sf:通常为4~6ms(SF稍后开始合成,刚好等App画完)
这两条信号就是你在Systrace里看到的彩色VSync条。它们的周期完全一致(16.6ms) ,但起始时间错开,形成流水线。
对比总结(纠正常见误区):
| 信号名称 | 产生者 | 是否硬件直接发出 | 用途 | 触发方式 |
|---|---|---|---|---|
| HW_VSYNC_0 | HWC/显示控制器 | ✅ 是 | 校准DispSync模型 | 硬件定时器,固定频率 |
| VSYNC-APP | DispSync(软件) | ❌ 否 | 触发Choreographer开始绘制 | 按需 + 模型预测 |
| VSYNC-SF | DispSync(软件) | ❌ 否 | 触发SurfaceFlinger合成 | 按需 + 模型预测 |
三、按需触发:没有绘制任务时,VSync去哪了?
这是Android VSync机制最容易误解的地方:Systrace里VSync-APP/SF信号并非每16.6ms都有。它们的出现遵循**按需申请(requestNextVsync)**原则。
3.1 为什么必须按需? 假设你打开一个静态文本页面:
- 屏幕内容完全没有变化。
- 如果DispSync仍然每16.6ms向App发送VSYNC-APP,App的主线程会持续每帧执行doFrame(),空转测量布局绘制------CPU空耗、发热、掉电。
- 完全没必要。
3.2 申请-触发机制
- App侧 :只有当
Choreographer.postCallback()被调用(如View.invalidate()、动画启动),才会通过Binder向SF的EventThread请求下一个VSYNC-APP。 - SF侧 :只有当BufferQueue中有新Buffer入队,或者窗口状态变化,才会请求下一个VSYNC-SF。
- DispSync侧 :只有当至少一个客户端在等待 时,它才会在预测的时钟点上发射软件VSync;如果无人申请,DispSync保持静默,整个图形系统进入低功耗状态。
这就是为什么Systrace里VSync信号时疏时密------动态场景(滑动、动画)密集出现;静态场景几乎消失。
3.3 硬件VSync的重校准 当DispSync长期依靠软件预测,可能会因温漂、时钟偏差而产生相位误差。如何修正?
- SurfaceFlinger在
postComposition()时会检查Present Fence(帧实际显示的时间戳)。 - 将这些Fence时间与SW_VSYNC预测时间对比,计算误差平方和。
- 若误差超过阈值(如1.5ms),DispSync会临时重新使能硬件VSync,采集3~6个新样本重新建模,然后再次关闭硬件VSync。
这是一套完整的闭环自适应系统:硬件提供基准,软件负责分发,误差触发校准。
四、全景流程图(建议收藏)

五、回答你最初的问题:"VSync是如何产生的?"
分三个层次回答:
- 物理层:显示控制器硬件定时器每16.6ms产生一个脉冲,HWC捕获后作为HW_VSYNC_0上报。
- 软件层 :SurfaceFlinger的DispSync模块将硬件信号作为"校准样本",通过锁相环算法拟合出一条持续运行的虚拟时钟线 ,并在这根时钟线上同时开出两条相位错开的信号通道(VSYNC-APP/VSYNC-SF)。
- 策略层 :这两条软件信号并非周期性自动发射 ,而是严格遵循按需申请机制------只有App或SF明确"我需要下一帧",DispSync才在预测时间点发射信号。
所以,VSync的最终形态------你在Systrace里看到的那些彩色节拍------是"硬件定时校准 + 软件预测生成 + 按需触发"三者结合的产物。 它不是纯粹的硬件中断,也不是纯粹的操作系统时钟,而是Android为移动场景定制的自适应、低功耗同步引擎。