1. 前言
忽然有一天,我想要做一件事:去代码中去验证那些曾经被"灌输"的理论。
-- 服装学院的IT男
本篇为 VSync 系列的第一篇,主要介绍软件 VSync 和理解节拍器。
本系列为之前学习 SurfaceFlinger 整理的一些笔记,现在分享出来,希望能帮助到有需要的同学。 代码基于 Android 13 ,虽然最新源码源码中部分逻辑已经了,但总体思路还是没有变的,不影响对 VSync 整体逻辑的理解。
VSync 系列目录:
1. SW-VSync简介
为什么不直接使用硬件 VSync (HW-VSYNC)?个人认为有以下两个原因:
-
- 如果 SurfaceFlinger 和每个应用都去监听硬件 VSync(HW-VSYNC) 无论从功耗上还是从模块设计上都是不妥的。
-
- 硬件(HW-VSYNC)是周期性的,因此软件层完全有可行性去模拟出一个等频率的软件 VSync(SW-VSYNC),于是有了下面这张图。

这张图来自 Google 官网(source.android.com/docs/core/g...
有一个"DispSync"可以基于 HW-VSYNC 来计算出对应的 SW-VSYNC ,这样软件层就接收 SW-VSYNC 最对应处理就好。 至于这里提到的"相位偏移","软件锁相回路 "是啥,暂时可以忽略,以黑盒的形式知道"DispSync"能算出 SW-VSYNC 即可。
这幅图中,"DispSync"计算的 SW-VSYNC 有两类:
- Choreographer 使用的 VSYNC
- SurfaceFlinger 使用的 SF_VSYNC
Choreographer 我们知道是 Java 层的一个类,控制应用绘制 UI 刷新节奏的,所以这里的第一种 VSYNC 就是给应用的。 第二种 SF_VSYNC 则是给 SurfaceFlinger 控制 Layer 合成的,
1.1 SW-VSync模型与计算
这块的理解可复杂可简单,复杂起来就是要去看模型的计算规则,是要哪些参数,相位差是多少,怎么计算的 SW-VSYNC 。既然是模拟的 HW-VSYNC 那必然还会有误差,出现误差后什么如何纠正等等。 但是如果不是从事 SurfaceFlinger 模块开发或者是刚了解这块的,建议以黑盒形式简单的了解这块。
-
- Google是以线性方程: y=bx+a 的方式来计算 SW-VSYNC
-
- 有一个反馈机制,当 SW-VSYNC 与 HW-VSYNC 差距达到阈值就开始重新采样计算纠正
1.2 SW-VSync 的分类
从上面的图可以看出 SW-VSync 分为了两类,这里再结合 Trace 看一下真正的 SW-VSync 有哪些:

-
- VSYNC-app :控制APP的UI渲染
-
- VSYNC-appsf :给APP用,感觉是用于调试的,暂时可以忽略这个
-
- VSYNC-sf : 控制SurfaceFlinger合成Layer
这里比较容易疑惑的是多出来了个 VSYNC-appsf ,这个目前也不太了解,因为 T 的代码对这块有重构,所以新增了这个 VSYNC-appsf ,感觉像是用于调试的,不过可以目前可以忽略这个,不影响后面的分析。 感兴趣的可以看[马哥的文章](blog.csdn.net/learnframew...
1.3 SW-VSync的产生
这里可能有三个问题:
-
- HW-VSync 是周期性一直存在的,那 SW-VSync 也一直存在吗?
-
- SW-VSync 是怎么分类的
-
- VSYNC-app ,VSYNC-appsf 和 VSYNC-sf 是同时触发的吗?
这三个问题将在本系列文章中一一解答,但目前需要先知道另一件事:SW-VSync 是如何产生的?
先抛开代码,举个栗子: 会看这篇文章的应该都是程序员,且在一线城市。以我所在的上海为例,每天上下班都要坐地铁,地铁的时间是很准时的,假设他的发车时间是全天固定的(早晚高峰肯定固定),以2分钟一趟为例,如果知道他的首发时间,知道某一趟到站的时间,是不是就可以计算到每一趟地铁到达你要坐的那个站的时间。
这个时间启动高德地图都能帮你算好了,所以只要打开地图,你就可以看到下一趟到站的地铁是什么时候,甚至下面几趟的都可以算好。不用去地铁站看站牌信息,在手机上就可以看到地铁到站的时间。
这个地铁到站的时间计算,和软件模型计算 SW-VSync 是类似的。
如果让程序员写一个逻辑,地铁要到站的时候就手机振动,或者发送一个Notification 通知用户,那你怎么做?
写个定时器呗,根据当前时间,和已知的下一趟地铁来的时间,计算出时间差,传给定时器,定时结束就可以做相应的事情通知用户了。
SW-VSync 的产生其实也就是通过定时器实现的,如果有应用或者 SurfaceFlinger 需要 VSync,就会发送请求,软件根据发送请求的时间,也就是当前时间(now)和它所预测的下一次 HW-VSync 时间,就能计算出还有多久 HW-VSync 要来了,假设是 10ms,那就开始设置一个 10ms 的定时任务,定时一到就触发回调,该干啥干啥。比如是 SurfaceFlinger 请求的 VSYNC 那就通知 SurfaceFlinger ,VSync 来了就可以开始干活了(合成Layer), 这个 VSync 就是 VSYNC-sf 。假设是应用请求的 VSync ,那定时结束就通知 app ,这个 VSync 就是 VSYNC-app 。 当然也可能2个同时申请了,那就都通知到,也就是既触发 VSYNC-sf 也触发 VSYNC-app 。
这里的描述,可能不是完全正确,硬扣细节肯定也是不严谨的,但是对于刚开始接触 VSync 来说,这样理解我觉得是可以先有这么一个概念,然后再通过实际的源码加深印象。
SW-VSync 是按需产生的,谁请求就给谁,都不请求就没人触发定时器了,自然也就不产生 SW-VSync 了。
一般来说肯定是 app 先请求绘制,绘制完了 SurfaceFlinger 再请求 VSync 来合成,所以绝大部分情况SurfaceFlinger 请求 VSync 的时候,定时器已经被 app 的请求触发了,所以 SurfaceFlinger 只需要等定时结束就好。
另外这里说的 app 是代表所有应用,而不是指某个应用,app 先收到 VSYNC-app ,然后再发送给具体需要的应用。
整个框架模型如下图所示:

其中 VSYNC-sf 是发送给 SurfaceFlinger 让其做 layer 的合成, VSYNC-app 则是还需要再做一次分发,只有具体的应用请求了才需要分发给这个应用。而应用端是由Choreographer来处理 Vsync 相关的事务。
这里的 VSYNC-appsf 用于测试,可以忽略,后面的分析一般也不会提到。
2. SW-VSYNC 的节拍器 VsyncDispatch 创建
前面提到 SW-VSYNC 其实目前有三个监听者: [VSYNC-app , VSYNC-appsf , VSYNC-sf] ,也大概介绍了 SW-VSYNC 的概念,那么撸代码先分析这个 3 个监听是如何注册的。
SW-VSync 模块中存在一个很重要的类,它就是号称 SW-VSYNC 的节拍器(VsyncDispatch),也有叫它信号泵的,顾名思义它会控制软件 VSync 的分发。
上面提到的三个监听都会被注册到 VsyncDispatch 子类 VSyncDispatchTimerQueue 下的 mCallbacks 对象中。
这一小节先来了解一下这个节拍器。
下面开始看代码:
开机过程中会启动 SurfaceFlinger 进程,经过一系列调用后会执行到 SurfaceFlinger::initScheduler 方法。
scss
# SurfaceFlinger.cpp
void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
// 避免重复初始化
if (mScheduler) {
mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
return;
}
// 是一个Fps对象,其中存储了刷新率fps和刷新周期period
const auto currRefreshRate = display->getActiveMode()->getFps();
mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
hal::PowerMode::OFF);
// 封装了不同刷新率下的VSync配置信息
mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
......
// 重点* 1.1 创建 Scheduler
mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this),
features);
{
auto configs = display->holdRefreshRateConfigs();
if (configs->kernelIdleTimerController().has_value()) {
features |= Feature::kKernelIdleTimer;
}
// 重点* 1.2 初始化 VsyncSchedule
mScheduler->createVsyncSchedule(features);
mScheduler->setRefreshRateConfigs(std::move(configs));
}
setVsyncEnabled(false);
mScheduler->startTimers();
const auto configs = mVsyncConfiguration->getCurrentConfigs();
const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
// 重点* 2.1 app 注册回调
mAppConnectionHandle =
mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
/*workDuration=*/configs.late.appWorkDuration,
/*readyDuration=*/configs.late.sfWorkDuration,
impl::EventThread::InterceptVSyncsCallback());
// 重点* 2.2 appSf 注册回调
mSfConnectionHandle =
mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
/*readyDuration=*/configs.late.sfWorkDuration,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
// 重点* 2.3 sf 注册回调
mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
configs.late.sfWorkDuration);
......
}
这里一共分为两大步:
-
- Scheduler 和 VsyncSchedule 的初始化
-
- app ,appsf,sf 注册回调
app ,appsf,sf 对应到 Trace 上就是 VSYNC-app、VSYNC-apps、VSYNC-sf
调用链:
arduino
SurfaceFlinger::initScheduler
Scheduler::init -- 创建 Scheduler
Scheduler::createVsyncSchedule -- 创建 VsyncSchedule
Scheduler::createConnection -- app appsf 注册回调
MessageQueue::initVsync -- sf 注册回调
2.1 Scheduler 和 VsyncSchedule 的初始化
在 SurfaceFlinger::initScheduler 方法的1.1处创建 Scheduler。
随后在1.2处执行了 Scheduler::createVsyncSchedule 方法,看方法名是创建一个 VsyncSchedule 。 这里涉及到 C++17 的语法,mVsyncSchedule 是定义在头文件的变量,然后执行 emplace 时会用参数用提供的 features 参数来触发 VsyncSchedule 对象的构建。
arduino
# Scheduler.h
std::optional<VsyncSchedule> mVsyncSchedule;
# Scheduler.cpp
void Scheduler::createVsyncSchedule(FeatureFlags features) {
mVsyncSchedule.emplace(features);
}
Scheduler 和 VsyncSchedule 都构建了,其中 VsyncSchedule 下面有三个重要的类。
ini
# VsyncSchedule.h
class VsyncSchedule {
......
private:
// 定义别名
using TrackerPtr = std::unique_ptr<VsyncTracker>;
using DispatchPtr = std::unique_ptr<VsyncDispatch>;
using ControllerPtr = std::unique_ptr<VsyncController>;
......
// 定义三个对象
TrackerPtr mTracker;
DispatchPtr mDispatch;
ControllerPtr mController;
}
# VsyncSchedule.cpp
// 构建时调用
VsyncSchedule::VsyncSchedule(FeatureFlags features)
: mTracker(createTracker()),
mDispatch(createDispatch(*mTracker)),
mController(createController(*mTracker, features)) {
if (features.test(Feature::kTracePredictedVsync)) {
mTracer = std::make_unique<PredictedVsyncTracer>(*mDispatch);
}
}
可以看到 VsyncSchedule 构建触发了三个重要的对象创建:
-
- VsyncTracker -- 训练软件 VSync 模型
-
- VsyncDispatch -- 节拍器,控制软件 VSync 的回调
-
- VsyncController -- 传递HWVsync,presentFence信号
这3个类在整个软件 VSync 的模块中是极其重要的,其中 VsyncDispatch 就是控制软件 VSync 的回调,这个很重要,其子类为:VSyncDispatchTimerQueue 。 目前先只看这个类的创建流程。
2.2 Vsync 节拍器的创建(VsyncDispatch)
创建 VsyncDispatch 的方法为 VsyncSchedule::createDispatch ,代码如下:
arduino
# VsyncSchedule.cpp
VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) {
using namespace std::chrono_literals;
// TODO(b/144707443): Tune constants.
constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us;
constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms;
// * 实际创建的是 VSyncDispatchTimerQueue
return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
kGroupDispatchWithin.count(),
kSnapToSameVsyncWithin.count());
}
-
- 传递了四个参数,第一个参数为构建了一个 Timer 对象,第二个参数传递了 VsyncTracker ,后面两个为时间,暂时不管。
-
- 实际上返回的是 VSyncDispatchTimerQueue 对象,其是 VsyncSchedule 子类
css
# VSyncDispatchTimerQueue.cpp
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
VSyncTracker& tracker, nsecs_t timerSlack,
nsecs_t minVsyncDistance)
: mTimeKeeper(std::move(tk)),
mTracker(tracker),
mTimerSlack(timerSlack),
mMinVsyncDistance(minVsyncDistance) {}
2.2.1 定时器 Timer
构建 VSyncDispatchTimerQueue 的时候传了一个 Timer 对象,Timer 是 TimeKeeper 的子类,然后被赋值给 VSyncDispatchTimerQueue 的 mTimeKeeper 变量,VsyncTracker 被赋值给 mTracker 。
这个 Timer 很重要,内部创建了一个线程,线程名为 "TimerDispatch" 专门控制软件层的 VSync ,节拍器的由来也是因为这个功能。
scss
# Timer.cpp
Timer::Timer() {
reset();
// 执行 threadMain 方法
mDispatchThread = std::thread([this]() { threadMain(); });
}
Timer 的父类是 TimeKeeper ,其父类是 Clock 的子类。
2.2.2 VSync 的定时的申请与结束处理
软件 VSync 的产生其实是靠一个定时器,也就是上的 Timer,既然是定时器那就必定有以下两个操作:
-
- 触发定时任务的方法
-
- 定时结束后的回调处理
这两个方法都定义在 VsyncDispatch 的子类 VSyncDispatchTimerQueue 中,对应代码如下:
javascript
# VSyncDispatchTimerQueue.cpp
// 申请Vsync(本质就是触发定时任务)
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
......// 触发定时任务
}
// 定时结束执行
void VSyncDispatchTimerQueue::timerCallback() {
....... // 遍历 mCallbacks 发送Vsync
}
这两个方法后面会详细介绍,应用和 SurfaceFlinger 如果想要接收到 VSync 信号则需要先申请 VSync,而申请 VSync 的流程必须要走到 VSyncDispatchTimerQueue::schedule 方法。
这个方法方法的最重要的事就是根据参数,触发一个定时任务。
定时结束后,其实就可以理解为马上要发送 SW-VSYNC 了,VSyncDispatchTimerQueue::timerCallback 方法会根据条件触发对应的 VSync 发送。
2.2.3 VSyncDispatchTimerQueue下 mCallbacks 的定义
"根据条件触发对应的 VSync 发送"是什么意思呢? 首先软件 VSync 是按需发送的,谁请求了才发给谁,所以会根据"条件"发送。 另外我们知道有 Trace 上就是 VSYNC-app、VSYNC-appsf、VSYNC-sf 三个 SW-VSYNC,也就是在 SurfaceFlinger::initScheduler 方法里看到触发了 app,appsf,sf 三个回调的注册。
这三个最终是注册到 VSyncDispatchTimerQueue 下定义的 mCallbacks 中,这个变量的定义如下:
arduino
# VSyncDispatchTimerQueue.h
class VSyncDispatchTimerQueue : public VSyncDispatch {
......
private:
using CallbackMap =
std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
CallbackMap mCallbacks GUARDED_BY(mMutex);
}
mCallbacks 的原来是一个键值对, key 为唯一标识 token,value 是 VSyncDispatchTimerQueueEntry 类型。
在 VSyncDispatchTimerQueue::timerCallback 方法中就是遍历 mCallbacks 下的元素,按需触发对应的回调,也就是触发响应的 VSYNC-app VSYNC-appsf VSYNC-sf 。
2.3 Vsync 节拍器小结
VSyncDispatchTimerQueue 相关的 UML 类图如下:

这里只是对 VSyncDispatchTimerQueue 这个类做了简单的介绍,后面的文章中还会看到其更多的东西,
-
- VSyncDispatchTimerQueue 是节拍器 VsyncDispatch 的子类
-
- VsyncDispatch 内部有个定时器(Timer)对象,维护了一个叫 "TimerDispatch" 专门控制软件层的 VSYNC
-
- 应用和 SurfaceFlinger 想要接收到 Vsync 都必须通过 VSyncDispatchTimerQueue::schedule 来申请(触发定时任务)
-
- 定时结束后触发 VSyncDispatchTimerQueue::timerCallback 方法
-
- VSyncDispatchTimerQueue 内部有个 mCallbacks 集合维护了对 SW-VSync 感兴趣的回调,也就是 应用(app)、SurfaceFlinger(sf)、和 appsf
-
- 通过 VSyncDispatchTimerQueue::registerCallback 方法将回调注册到 mCallbacks 中,这个方法定义在父类中
-
- VSyncDispatchTimerQueue 下面的 mCallbacks 里的元素是 VSyncDispatchTimerQueueEntry (这个类也很复杂,目前类图里先省略内容)
3. 补充:dump 软件 Vsync 回调集合
虽然还没分析具体代码,但是已经多次给出结论:app appsf sf 都在 VSyncDispatchTimerQueue::mCallbacks 下。 为了确认和再加深这个结论,可以使用以下命令对 SurfaceFlinger 进行 dump:
adb shell dumpsys SurfaceFlinger >sf.txt
输出和回调相关的内容如下:
yaml
VsyncDispatch:
Timer:
DebugState: Waiting
...... // 忽略
Callbacks:
appSf:
workDuration: 16.67ms readyDuration: 27.60ms earliestVsync: -525582.81ms relative to now
mLastDispatchTime: 525566.12ms ago
sf:
workDuration: 27.60ms readyDuration: 0.00ms earliestVsync: -56083.53ms relative to now
mLastDispatchTime: 55935.26ms ago
app:
workDuration: 20.00ms readyDuration: 27.60ms earliestVsync: -32569.80ms relative to now
mLastDispatchTime: 32553.14ms ago
可以看到 "Callbacks:" 下有三个对象,就是上面提到的 app、appsf、sf。
对应控制输出的代码如下:
scss
# VSyncDispatchTimerQueue.cpp
void VSyncDispatchTimerQueue::dump(std::string& result) const {
std::lock_guard lock(mMutex);
StringAppendF(&result, "\tTimer:\n");
mTimeKeeper->dump(result);
StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
mMinVsyncDistance / 1e6f);
StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
(mIntendedWakeupTime - mTimeKeeper->now()) / 1e6f);
StringAppendF(&result, "\tmLastTimerCallback: %.2fms ago mLastTimerSchedule: %.2fms ago\n",
(mTimeKeeper->now() - mLastTimerCallback) / 1e6f,
(mTimeKeeper->now() - mLastTimerSchedule) / 1e6f);
StringAppendF(&result, "\tCallbacks:\n");
// 内容就是 mCallbacks 集合下的内容
for (const auto& [token, entry] : mCallbacks) {
entry->dump(result);
}
}
我们已经知道这个 mCallbacks 下的元素就是3个 VSyncDispatchTimerQueueEntry 实例,所以具体的 dump 要看 VSyncDispatchTimerQueueEntry 的实现。 VSyncDispatchTimerQueueEntry 定义在 VSyncDispatchTimerQueue.cpp 下面。
swift
# VSyncDispatchTimerQueue.cpp
void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
std::lock_guard<std::mutex> lk(mRunningMutex);
std::string armedInfo;
if (mArmedInfo) {
StringAppendF(&armedInfo,
"[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
(mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
(mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
(mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
}
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
StringAppendF(&result,
"\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
"to now\n",
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
(mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
(systemTime() - *mLastDispatchTime) / 1e6f);
} else {
StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
}
}
4. 总结
本篇介绍了个人对 SW-Vsync 的理解,另外重点介绍了 VsyncDispatch 和其子类 VSyncDispatchTimerQueue 。
下一篇将详细分析 SurfaceFlinger::initScheduler 方法中三个对象(app, appsf, sf)是如何把自己注册到 VSyncDispatchTimerQueue 下的 mCallbacks 中的。
本篇涉及到的调用链如下:
arduino
SurfaceFlinger::initScheduler
Scheduler::init -- Scheduler初始化
Scheduler::createVsyncSchedule -- VsyncSchedule 初始化
VsyncSchedule::emplace
VsyncSchedule::init
VsyncSchedule::createTracker -- VsyncTracker
VsyncSchedule::createDispatch -- VsyncDispatch(节拍器)
Timer::Timer -- 定时器创建
VSyncDispatchTimerQueue::init
VsyncSchedule::createController -- VsyncController
Scheduler::createConnection -- app appsf 注册回调
MessageQueue::initVsync -- sf 注册回调
参考:
<千里马学框架-SurfaceFlinger>