[Android禅修之路] 解读Vsync(二)
上篇 解读Vsync(一) 说明了 Vsync 信号在 SurfaceFlinger 中的传递过程,最后留下了一个问题,就是 SurfaceFlinger 中的事件是怎么来的。这个事件是怎么从硬件信号转变成 SurfaceFlinger 中的事件的?
本篇围绕着这个问题展开,大致如下
- Vsync 信号源 DispSync [节四和节五]
- Vsync 信号的控制器 EventControlThread [节六合节七]
- 硬件 Vsync 到 软件 Vsync 的转换 [节八]
四 DispSync
前面我们说过了,Vsync 事件是通过 DispSync 产生的。在 SurfaceFlinger 的初始化的过程中,会创建一个 DispSync,当时对它的描述是这个 DispSync 就是 Vsync 的信号源,其实准确来说,它是一個模擬 HW-VSYNC 的軟件模型。
首先说为什么需要 DispSync。
我们知道 Vsync 信号是通过硬件产生的。当我们的应用和系统接收到 Vsync 信号之后应该怎么做?如果马上开始显示相关的刷新工作,那么必然会造成 CPU 和 GPU 工作的拥挤。理想的情况是,系统的刷新工作执行一会儿,我们的应用的刷新工作执行一会儿,两者交互的进行,这样才能让系统执行的效率最大化。这种情况下就需要 DispSync 了。如下,就是 Google 官方提供的 DispSync 示意图,它的作用就是将 Vsync 信号转换为 SF-VSYNC 和 APP-VSYNC。
4.1 DispSync 的创建
DispSync 是在在 Scheduler 的构造函数中创建的, 代码如下
[frameworks/native/services/surfaceflinger/Scheduler/Scheduler.cpp]
auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
mPrimaryDispSync = std::move(primaryDispSync);
4.2 DispSync::init
在 DispSync 创建时,调用了它的初始化函数 init。
[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
mIgnorePresentFences = !hasSyncFramework;
mPresentTimeOffset = dispSyncPresentTimeOffset;
// 开启了一个名为 DispSync 的线程,另外的两个参数是线程的优先级,
// 分别是 PRIORITY_URGENT_DISPLAY(-8)和PRIORITY_MORE_FAVORABLE(-1),
// 所以它的线程优先级和 SF 是一样的-9。我们知道优先级的数字越低,代表优先级越高,
// 所以这里的 DispSync 线程优先级是非常高的
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
struct sched_param param = {0};
param.sched_priority = 2;
// 设置 SCHED_FIFO 尽量提高 DispSyncThread 的优先级,以减少误差
if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
}
reset();
beginResync();
if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0);
}
}
DispSync 的 init 函数中调用了 mThread 的 run,这个 mThread 是一个 DispSyncThread。
DispSyncThread 是 DispSync 的一個内部类,它是 Thread 的子类,主要功能就是模拟 HW-VSYNC 信号。 这个线程大多数时候都是处于阻塞状态。
它会算出下一个 Vsync 信号的时间节点,然后周期性的醒来并通知 Vsync 的监听器 Listener。
五 DispSyncThread
DispSyncThread 继承自 Thread,定义如下
[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
class DispSyncThread : public Thread
// 构造函数
DispSyncThread(const char* name, bool showTraceDetailedInfo)
: mName(name),
mStop(false),
mModelLocked(false),
mPeriod(0),
mPhase(0),
mReferenceTime(0),
mWakeupLatency(0),
mFrameNumber(0),
mTraceDetailedInfo(showTraceDetailedInfo) {}
注意,这里的 mPeriod 和 mPhase 在构造函数中都是0。
- mPeriod 代表 Vsync 信号的周期,也就是两个 Vsync 信号之间的时间差,如果每秒60帧那它就是 16.6667 ms
- mPhase 代表 Vsync 信号周期的初始偏移,因为我们需要错开 sf 和 app 的 Vsync 信号,所以需要加上对应的初始化偏移
DispSyncThread 它是 Thread 的子类,它的工作函数就是 threadLoop,这个函数就是产生 Vsync 信号的函数
5.1 threadLoop 的第一部分
threadLoop 这个函数可以分为两个部分,因为 DispSyncThread 在创建的时候 mPhase 还是0,所以这里我们先看第一部分
virtual bool threadLoop() {
status_t err;
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// 死循环
while (true) {
std::vector<CallbackInvocation> callbackInvocations;
nsecs_t targetTime = 0;
{ // 自动锁
Mutex::Autolock lock(mMutex);
++mFrameNumber;
if (mStop) {
return false;
}
// 如果 mPeriod 为0,则会在这里处于等待状态
// 在 DispSyncThread 的构造函数中,它被初始化为0,也就是说一开始它是处于等待状态
if (mPeriod == 0) {
err = mCond.wait(mMutex);
if (err != NO_ERROR) {
return false;
}
continue;
}
...
第一部分很简单,我们看到一个死循环,然后在这个循环中会判断 mPeriod 的值,如果它为0就会陷入等待,也就是说线程在等待其他地方来修改这个 mPeriod 的值,来触发这个线程继续执行的逻辑。
关于 mPeriod 的修改,其实是由 SurfaceFlinger 触发的。我们知道 SurfaceFlinger 是 Android 系统中非常重要的一个系统服务,所以在系统启动的时候,会跟着启动 SurfaceFlinger 这个系统服务,关于 SurfaceFlinger 的启动,可以查看 SurfaceFlinger的启动
在 Android 启动系统服务的时候,经常会调用一个函数用来绑定 Binder 的生命周期,用来处理 Binder 挂掉的情况,这个函数就是 binderDied
5.2 SurfaceFlinger
5.2.1 SurfaceFlinger::binderDied
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
{
// 初始化显示设备
initializeDisplays();
// 启动开机动画
startBootAnim();
}
在 binderDied 中做了两件事,一个是显示设备的初始化,一个是显示开机动画。开机动画不是本篇的重点,这里我们看它是如何初始化显示设备的
5.2.2 SurfaceFlinger::initializeDisplays
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::initializeDisplays() {
// 异步处理,调用 onInitializeDisplays
postMessageAsync(
new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
}
5.2.3 SurfaceFlinger::onInitializeDisplays
onInitializeDisplays 就是初始化显示设备的参数。
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::onInitializeDisplays() {
const auto display = getDefaultDisplayDeviceLocked();
if (!display) return;
const sp<IBinder> token = display->getDisplayToken().promote();
// 重设屏幕参数
Vector<ComposerState> state;
Vector<DisplayState> displays;
DisplayState d;
d.what = DisplayState::eDisplayProjectionChanged |
DisplayState::eLayerStackChanged;
d.token = token;
d.layerStack = 0;
d.orientation = DisplayState::eOrientationDefault;
d.frame.makeInvalid();
d.viewport.makeInvalid();
d.width = 0;
d.height = 0;
displays.add(d);
setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});
// 设置电源模式,见[5.2.5]
setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
// 获取 Vsync 的 Period
const nsecs_t vsyncPeriod = getVsyncPeriod();
// 设置 Vsync 的 Period
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
...
}
onInitializeDisplays 函数有点长,但逻辑并不复杂,它主要都是获取显示设备的参数并且设置,然后获取 Vsync 的 Period。
看到这我们就明白了,因为 Vsync 涉及到显示的刷新率,所以系统需要先知道显示设备支持的刷新率是多少,是60帧还是120帧。所以必须在初始化显示设备的时候来获取这个 Period,因为 Period 就是一帧需要的时间。如果每秒60帧, 那么 period 就是16.6667ms
5.2.4 SurfaceFlinger::getVsyncPeriod
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
nsecs_t SurfaceFlinger::getVsyncPeriod() const {
const auto displayId = getInternalDisplayIdLocked();
if (!displayId || !getHwComposer().isConnected(*displayId)) {
return 0;
}
// 获取 displayId 对应显示设备的 config,然后再通过 config 拿到对应的刷新时间
// 这里 getHwComposer 获取到的就是 HWComposer
// 拿到的 Config其实就是 HWC2::Display::Config
const auto config = getHwComposer().getActiveConfig(*displayId);
return config ? config->getVsyncPeriod() : 0;
}
5.2.5 SurfaceFlinger::setPowerModeInternal
在 getVsyncPeriod 之前,还有一个函数 setPowerModeInternal,函数名的意思是设置电源模式,它的任务也就是处理电源模式的切换,电源的模式有很多,我们这里值列举两个比较重要的息屏和亮屏
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
// 判断是否是虚拟显示
if (display->isVirtual()) {
return;
}
// 获取显示设备的id
const auto displayId = display->getId();
// 获取电源模式
int currentMode = display->getPowerMode();
// 如果模式没有发生改变,就直接返回
if (mode == currentMode) {
return;
}
// 设置显示设备的电源模式
display->setPowerMode(mode);
// 如果存在 mInterceptor,则修改 mInterceptor 中的电源模式
if (mInterceptor->isEnabled()) {
mInterceptor->savePowerModeUpdate(display->getSequenceId(), mode);
}
// 注意,下面的判断分别判断了 currentMode 和 mode
// currentMode 是当前的电源状态,mode 是电源修改的目标状态
if (currentMode == HWC_POWER_MODE_OFF) {
// 如果 currentMode 是关,那么首先要做的肯定需要开启显示器,也就是亮屏
// 修改 HwComposer 的电源模式
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
// 调用 Scheduler 的 onScreenAcquired,最后会调用到 EventThread 的 onScreenAcquired
// 这里传递的是 App 的 ConnectionHandle,也就是说会调用到 App 的 EventThread
mScheduler->onScreenAcquired(mAppConnectionHandle);
// 调用 Scheduler 的 resyncToHardwareVsync,这里又获取了一次 getVsyncPeriod,
// 并且将 Period 作为参数传递了进去
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
mVisibleRegionsDirty = true;
mHasPoweredOff = true;
// 亮屏了,当然要重绘所有的事物
repaintEverything();
struct sched_param param = {0};
param.sched_priority = 1;
} else if (mode == HWC_POWER_MODE_OFF) {
// 如果 mode 是关,那么说明要息屏
struct sched_param param = {0};
// 关闭 Display,当然需要关闭 Vsync
if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
// SF 将停止绘制工作
} ...
if (display->isPrimary()) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats.setPowerMode(mode);
}
}
这里我们先看逻辑简单的 repaintEverything
5.2.6 repaintEverything
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::repaintEverything() {
mRepaintEverything = true;
signalTransaction();
}
5.2.7 signalTransaction
[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::signalTransaction() {
mScheduler->resetIdleTimer();
// 通知 SurfaceFlinger 中的消息队列进行重绘
mEventQueue->invalidate();
}
六 Scheduler
Scheduler 就是 SurfaceFlinger 中的调度器,它里面有很多重要的信息,我们先看它的构造函数
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig)
: mHasSyncFramework(running_without_sync_framework(true)),
mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
mRefreshRateConfigs(refreshRateConfig) {
// 注意:临时使用real DispSync实现类型创建一个本地临时文件,
// 以便在使用接口类型将其存储以供更一般的使用之前,可以使用配置的值对其进行初始化。
auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
mPrimaryDispSync = std::move(primaryDispSync);
// 创建了一个 EventControlThread 对象,参数是外界传递进来的 function
// 这个 function 是 impl::EventControlThread::SetVSyncEnabledFunction
// 这个 funtion 其实就是 SF 在 init 时传递进来的 SF 中的函数 setPrimaryVsyncEnabled
mEventControlThread = std::make_unique<impl::EventControlThread>(function);
...
}
SurfaceFlinger 中的 setPrimaryVsyncEnabled,其实就是设置 HwComposer 的 Vsync 开关,也就是硬件的 Vsync
void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
ATRACE_CALL();
Mutex::Autolock lock(mStateLock);
if (const auto displayId = getInternalDisplayIdLocked()) {
getHwComposer().setVsyncEnabled(*displayId,
enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
}
}
6.1 Scheduler::resyncToHardwareVsync
接下来我们接着看修改电源模式 setPowerModeInternal 中,调用而来的 resyncToHardwareVsync
void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
{
std::lock_guard<std::mutex> lock(mHWVsyncLock);
// setPowerModeInternal 中传递进来的参数是 true,所以这里 mHWVsyncAvailable 也被设置为 true
// 也就是说使用硬件 Vsync
if (makeAvailable) {
mHWVsyncAvailable = makeAvailable;
} else if (!mHWVsyncAvailable) {
// 硬件 Vsync当前不可用,因此暂时中止重新同步尝试
return;
}
}
// 如果刷新时间小于0,显然是错误的,我们假定系统是每秒60帧,那么这里的 period 应该是 16.6667ms
if (period <= 0) {
return;
}
// 设置 Vsync 的周期时间
setVsyncPeriod(period);
}
6.2 Scheduler::setVsyncPeriod
void Scheduler::setVsyncPeriod(const nsecs_t period) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
// 调用 mPrimaryDispSync 的 setPeriod 设置刷新时间,前面我们一家知道了 mPrimaryDispSync 其实就是 DispSync
// 所以这里其实调用的是 DispSync 的 setPeriod
mPrimaryDispSync->setPeriod(period);
// mPrimaryHWVsyncEnabled 是在 Scheduler 的构造函数中被设置为 false 的
if (!mPrimaryHWVsyncEnabled) {
mPrimaryDispSync->beginResync();
// 调用 EventControlThread 的 setVsyncEnabled [6.4.3]
mEventControlThread->setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
6.3 DispSync::setPeriod
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
mPendingPeriod = period;
}
在 DispSync 的 setPeriod 中,修改了 mPendingPeriod 的值为 period。还记得之前因为 period 为0而陷入的等待吗,这里终于将 period 的值改变了。
接下来我们看看这个 EventControlThread 是如何打开 Vsync 开关的。
6.4 EventControlThread
通过刚才的代码,我们已经能猜到,这个 EventControlThread 其实就是控制 Vsync 的开关。首先我们来看这个控制器的定义
6.4.1 EventControlThread 的定义
class EventControlThread final : public android::EventControlThread {
std::thread mThread{&EventControlThread::threadMain, this};
}
EventControlThread 的定义比较简单,但是在它的最后一行有一个 mThread,这个 mThread 执行的就是 EventControlThread 的 threadMain 函数
6.4.2 EventControlThread的构造函数
EventControlThread 的构造函数则是设置了线程的名字和优先级。接下来我们继续之前的分析
EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
: mSetVSyncEnabled(function) {
// 设置线程名
pthread_setname_np(mThread.native_handle(), "EventControlThread");
pid_t tid = pthread_gettid_np(mThread.native_handle());
// 设置优先级
setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
set_sched_policy(tid, SP_FOREGROUND);
}
6.4.3 EventControlThread::setVsyncEnabled
void EventControlThread::setVsyncEnabled(bool enabled) {
std::lock_guard<std::mutex> lock(mMutex);
mVsyncEnabled = enabled;
mCondition.notify_all();
}
在 setVsyncEnabled 中修改了 mVsyncEnabled,并且调用了 mCondition 的 notify_all,之前已经说了 EventControlThread 中有一个 Thread,想必这个通知就会回调到这个 Thread 中
6.4.4 EventControlThread::threadMain
void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
auto keepRunning = true;
auto currentVsyncEnabled = false;
// 死循环
while (keepRunning) {
mSetVSyncEnabled(currentVsyncEnabled);
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
});
currentVsyncEnabled = mVsyncEnabled;
keepRunning = mKeepRunning;
}
}
threadMain 中只做了一件事,就是修改 currentVsyncEnabled,并且将他传递到 mSetVSyncEnabled。看来和这个类的名字一样,这真的是一个 Event 的控制线程。
至于这个 mSetVSyncEnabled 函数,我们在构造函数中就已经看到,它其实就是外面传递进来的 function,而在最开始,我们已经看到了在 SF 的 init 函数中,传递进来的 setPrimaryVsyncEnabled,所以这里最终调用到的就是 SF 的 setPrimaryVsyncEnabled。
七 setPrimaryVsyncEnabled 的调用过程
setPrimaryVsyncEnabled 首先调用到 SurfaceFlinger 中,然后在 SurfaceFlinger 中,又调用到 HWComposer,这里设置的 Vsync 就是硬件的 Vsync。
7.1 SurfaceFlinger::setPrimaryVsyncEnabled
void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
Mutex::Autolock lock(mStateLock);
if (const auto displayId = getInternalDisplayIdLocked()) {
// 获取 HwComposer,设置 Vsync 的 enabled
getHwComposer().setVsyncEnabled(*displayId,
enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
}
}
7.2 HWComposer::setVsyncEnabled
void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
if (displayData.isVirtual) {
LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
return;
}
// 注意:我们在这里使用我们自己的内部锁,因为我们必须在锁保持的情况下调用HWC,
// 并且我们希望确保即使HWC阻塞(它不应该阻塞),它也不会影响其他线程。
std::lock_guard lock(displayData.vsyncEnabledLock);
if (enabled == displayData.vsyncEnabled) {
return;
}
// 调用 hwcDisplay 的 setVsyncEnabled
auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
RETURN_IF_HWC_ERROR(error, displayId);
displayData.vsyncEnabled = enabled;
}
在 HWComposer 中,会设置硬件是否产生 Vsync 信号。到此,我们也知道了 Vsync 信号是通过 HWComposer 产生的了。
7.3 HWComposer 的初始化
但是我们还是没有弄清楚 Vsync 的硬件信号是如何变成软件信号的。
但是我们这里看到的都是硬件的 Vsync 信号,它有是如何变成 EventThread 中的 Event 的呢。
我们回到 SurfaceFlinger 的 init 函数中,在 init 函数中有这样两行代码
SurfaceFlinger::init(){
// 通过 Factory 创建 HWComposer,并将创建的 HWComposer 设置到 CompositionEngine
// 这里的 getBE 获取到的就是 SurfaceFlingerBE 对象,它是 SF 中的一个内部类,封装了和 HWComposer 相关的逻辑
mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
// 给 HWComposer 注册回调,回调函数就是 this(SurfaceFlinger 自己)
mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
}
getFactory().createHWComposer(getBE().mHwcServiceName) 就是创建 HWComposer。传递进去的就是 HWComposer 的服务名称。
7.3.1 HWComposer 的创建
首先将传递进来的字符串作为 serviceName 创建了一个 HWComposer
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override {
return std::make_unique<android::impl::HWComposer>(
std::make_unique<Hwc2::impl::Composer>(serviceName));
}
7.3.2 HWComposer::registerCallback
void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
int32_t sequenceId) {
// 这个 mHwcDevice 是 HWC2::Device
mHwcDevice->registerCallback(callback, sequenceId);
}
在创建完之后,SurfaceFlinger 又将自己作为回调对象传递到了 HWComposer 中,对应的序列id是通过 SurfaceFlingerBE 获取来的。也就是显示硬件的序列号。
7.3.3 Device::registerCallback
void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
if (mRegisteredCallback) {
ALOGW("Callback already registered. Ignored extra registration "
"attempt.");
return;
}
mRegisteredCallback = true;
// 创建了一个回调的连接桥,封装了 callback(即Sf)和对应的序列id
sp<ComposerCallbackBridge> callbackBridge(
new ComposerCallbackBridge(callback, sequenceId));
// 将 callbackBridge 设置到 mComposer 中
// 这个 mComposer 定义在 HWC2.h 中,是 android::Hwc2::Composer
// 它的实现在 ComposerHal.cpp
mComposer->registerCallback(callbackBridge);
}
7.3.4 Composer::registerCallback
void Composer::registerCallback(const sp<IComposerCallback>& callback)
{
// 这里有一个 mClient,它其实就是硬件抽象层在 SurfaceFlinger 中的对象 IComposerClient
auto ret = mClient->registerCallback(callback);
}
7.4 ComposerCallbackBridge
之前已经说了 ComposerCallbackBridge 是对硬件 Vsync 回调的封装,那么我们接下来就看它是如何进行封装的,首先还是看 ComposerCallbackBridge 的定义
class ComposerCallbackBridge : public Hwc2::IComposerCallback {
public:
ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
: mCallback(callback), mSequenceId(sequenceId) {}
Return<void> onHotplug(Hwc2::Display display,
IComposerCallback::Connection conn) override
{
HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
mCallback->onHotplugReceived(mSequenceId, display, connection);
return Void();
}
Return<void> onRefresh(Hwc2::Display display) override
{
mCallback->onRefreshReceived(mSequenceId, display);
return Void();
}
Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
{
mCallback->onVsyncReceived(mSequenceId, display, timestamp);
return Void();
}
private:
ComposerCallback* mCallback;
int32_t mSequenceId;
};
显然,当系统硬件发出 Vsync 信号时,会通过 ComposerCallbackBridge 调用到它的 mCallback 的 onVsyncReceived。而这个 mCallback 之前已经说了,就是 SurfaceFlinger 对象,所以最终系统硬件层的 Vsync 信号,会通过 SurfaceFlinger 的 onVsyncReceived 函数进行传递。
到这里我们已经知道硬件的 Vsync 信号是如何传递到 SurfaceFlinger 中的。那么还有一个问题,这个是如何变成软件信号的?
八 Vsync 模拟信号的产生
首先我们来看 SurfaceFlinger 接收到硬件 Vsync 信号后做的事情。
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
int64_t timestamp) {
Mutex::Autolock lock(mStateLock);
// 首先判断序列号是否匹配,如果不匹配则忽略
if (sequenceId != getBE().mComposerSequenceId) {
return;
}
// 然后通过 HwComposer 过滤不需要的 Vsync 信号,正常情况结果为 true
if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
return;
}
// 是否是外部显示器的 Vsync。Android 虽然支持外部显示器,但是手机并没有外部显示器
if (hwcDisplayId != getHwComposer().getInternalHwcDisplayId()) {
return;
}
// 调用 Scheduler 的 addResyncSample
bool periodChanged = false;
mScheduler->addResyncSample(timestamp, &periodChanged);
if (periodChanged) {
// 如果刷新率改变了,还需要调用 onRefreshRateChangeDetected
mVsyncModulator.onRefreshRateChangeDetected();
}
}
8.1 Scheduler::addResyncSample
首先是调用调度器的 addResyncSample,然后在调度器里面,调用了 DispSync 的 addResyncSample
void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
bool needsHwVsync = false;
*periodChanged = false;
{
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
// 调用 DispSync::addResyncSample [8.2]
needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
}
}
if (needsHwVsync) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
8.2 DispSync::addResyncSample
addResyncSample 这个函数非常复杂,并且它其中调用到的 updateModelLocked 也是一个非常关键的函数。这两个函数运用到了一些数学知识,所以在理解这两个函数之前,我们先看其中定义的宏
// 最多记录多少个 Vsync 采样点,定义的是32个
enum { MAX_RESYNC_SAMPLES = 32 };
// 最少要多少个 Vsync 采样点才开始更新刷新时间,定义是6个
enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };
enum { NUM_PRESENT_SAMPLES = 8 };
enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 };
理解了其中的宏,我们开始逐步分析 addResyncSample
bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) {
Mutex::Autolock lock(mMutex);
// 刷新率是否改变了
*periodChanged = false;
// 首先用 mFirstResyncSample 加 mNumResyncSamples 然后对 MAX_RESYNC_SAMPLES 取余
// 这里系统最多会记录32个采样点,mNumResyncSamples 代表采样点的数量,它是不断自增的
// 而 mFirstResyncSample 则表示这32个采样点中,第一个采样点的序号是多少
// 假设现在第一个采样点是idx是6,已经有32个采样点,那么这32个采样点的idx就应该是6-37号
// 所以这里取余得到的结果,应该是最后一个采样点的序号加1,也就是下一个采样点的序号38,取余之后为6
// 所以这里会将第一个采样点idx的时间戳覆盖
const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
// 将传递进来的 timestamp 设置为6号采样点的时间戳
mResyncSamples[idx] = timestamp;
// 如果 mNumResyncSamples 为0,说明是第一次收到 Vsync 信号
if (mNumResyncSamples == 0) {
mPhase = 0;
} else if (mPendingPeriod > 0) {
// mNumResyncSamples > 0, so priorIdx won't overflow
// 这里是先减1再取余,也就是最后一个采样点的序号,即37号采样点
const size_t priorIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
// 拿到41号采样点的时间戳
const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
// 用42号采样点的时间戳减去41号采样点的时间戳,也就是最后两个采样点的时间戳之差
const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
// mPeriod 是一帧应该花费的时间,之前我们假定是60帧,它就是16.6667ms
// mPendingPeriod 则是到下一个 Vsync 信号需要等待的时间
// observedVsync 是两个采样点之间的时间差,理想情况,采样点的时间差应该和每帧的时间 mPeriod 相同
// 但是因为 Vsync 信号是硬件发出的,处理硬件信号需要花费时间,所以实际运行时,这两个时间是不同的
if (std::abs(observedVsync - mPendingPeriod) < std::abs(observedVsync - mPeriod)) {
// 即 mPendingPeriod > mPeriod
// 观察到的 Vsync 更接近挂起期间,因此重置模型并刷新挂起期间。
resetLocked();
mPeriod = mPendingPeriod;
mPendingPeriod = 0;
*periodChanged = true;
}
}
// 用最新的时间戳更新参考时间
mReferenceTime = timestamp;
// 通知 Thread 收到 Vsync 信号
// 调用 DispSync 中的 mThread 的 updateModel,即 DispSyncThread::updateModel
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
// 更新 mNumResyncSamples 或 mFirstResyncSample 的值
// 如果采样点的数量没有到32个,则 mNumResyncSamples 自增
// 如果到达了32个,则需要将第一个采样点的序号后移一位,之前第一个采样点的idx是6,这样后移了一位之后,
// 第一个采样点的idx就变成了7
if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
mNumResyncSamples++;
} else {
mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
}
// 重要函数,计算刷新时间
updateModelLocked();
if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
resetErrorLocked();
}
if (mIgnorePresentFences) {
// 如果我们忽略了当前的fences,我们就无法知道我们是否与HW-vsync同步,所以我们只是请求打开HW-vsync事件。
return true;
}
// Check against kErrorThreshold / 2 to add some hysteresis before having to resync again
// 检查kErrorThreshold/2以在再次重新同步之前添加一些滞后
bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
if (modelLocked) {
mThread->lockModel();
}
return !modelLocked;
}
这里有两个比较重要的函数,一个是 DispSyncThread::updateModel,一个是 updateModelLocked。我们先看 updateModelLocked
8.3 DispSync::updateModelLocked
updateModelLocked 这个函数会对 Vsync 信号的时间进行校准,我们知道 Vsync 信号是由硬件发出的,那么从硬件发出的 Vsync 信号到软件接收到,这中间会花费一定的时间,如果我们希望软件的 Vsync 信号和硬件的同步,那么就需要知道从硬件信号到软件信号花费了多少时间。
我们假设一个 Vsync 周期是一个圆,这个圆的角的弧度是 2π,这个圆的周长是 mPeriod,即一个周期ms占用的角是2π弧度。
这里我们假设系统是每秒60帧,那么一帧的时间就是16.6667个单位时间ms,即16.6667个单位时间ms对应的弧度是2π。如上图所示。
那么现在我们要求一个单位时间ms对应的弧度是多少呢?也就是这个秒针转动了多少。这里假定为 scale,公式如下:
结果就是:
即一个单位ms对应角的弧度是 , 理解了这些,我们再来看下面的代码
double scale = 2.0 * M_PI / double(mPeriod);
首先计算一个单位时间ms对应在圆上的角,所占用的弧度 scale。
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
mFirstResyncSample 是第一个 Vsync 采样点的序列号,而用它加1,就是从第二个采样点开始
nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
mReferenceTime 是最新采样点的时间戳,每次收到 Vsync 信号时,都会更新 mReferenceTime。
mResyncSamples[idx] - mReferenceTime 得到的就是每个采样点到现在时间戳的具体时间 sample,它应该是 mPeriod * n ± k,其中n是正整数,k是常数
double samplePhase = double(sample % mPeriod) * scale;
用 sample 对 mPeriod 取余,就是得到这个±k,这个k是一个具体的时间,单位为ms,然后用它乘以 scale,就得到了这个k在这个周期圆上对应角的弧度 samplePhase。
然后再对这个弧度求三角函数,假设这个误差的弧度为α:
sampleAvgX += cos(samplePhase);
sampleAvgY += sin(samplePhase);
最后对这个弧度取三角函数,得到X和Y坐标,并且相加, sampleAvgX 就是这个弧度的终点的X坐标,sampleAvgY 就是Y坐标
sampleAvgX /= double(mNumResyncSamples - 1);
sampleAvgY /= double(mNumResyncSamples - 1);
然后循环所有采样点的集合,也就是32个,得到X坐标的和和Y坐标的和,然后用这个和除以 mNumResyncSamples - 1,因为我们是从第二个采样点开始循环的,所有少一个采样点,得到平均坐标 sampleAvgX 和 sampleAvgY。最后求出这32个采样点的平均误差mPhase
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
通过反三角函数 atan2 得到这个点角对应的弧度 α',然后除以 scale,scale 表示一个单位时间ms所占用角的弧度,用 α' 除以 scale ,得到 mPhase 就表示这个角对应的弧度 α' 占用了多少个单位时间ms。
这里得到的 mPhase 就是一个周期的误差的时间,也就是从硬件信号到软件信号的耗时。它是通过32个采样点计算得来的。
void DispSync::updateModelLocked() {
// 只有当采样点的数量大于6个的时候,才会做校准操作,也就是样本数量太少则不校准
if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
nsecs_t durationSum = 0;
nsecs_t minDuration = INT64_MAX;
nsecs_t maxDuration = 0;
// 这里计算总时长,以及拿到最长和最短的硬件vsync间隔
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
// 这里看成 (idx - 1 + MAX_RESYNC_SAMPLES) % MAX_RESYNC_SAMPLES 更好理解
// 即 idx 的前一个采样点
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
// 两个采样点的时间差
nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
// 增加到总时间差里面
durationSum += duration;
minDuration = min(minDuration, duration);
maxDuration = max(maxDuration, duration);
}
// 这里用总时间差,减去最大和最小时间差后,计算平均间隔 mPeriod
// 也就是每两个采样点之间平均花费的时间
durationSum -= minDuration + maxDuration;
mPeriod = durationSum / (mNumResyncSamples - 3);
// 采样点的X和Y坐标
double sampleAvgX = 0;
double sampleAvgY = 0;
// 2π 除以 mPeriod
double scale = 2.0 * M_PI / double(mPeriod);
// 跳过第一个Vsync,因为第一个Vsync已经更新到DispSync中了。
// mReferenceTime是第一个Vsync的时间
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
double samplePhase = double(sample % mPeriod) * scale;
sampleAvgX += cos(samplePhase);
sampleAvgY += sin(samplePhase);
}
sampleAvgX /= double(mNumResyncSamples - 1);
sampleAvgY /= double(mNumResyncSamples - 1);
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
if (mPhase < -(mPeriod / 2)) {
mPhase += mPeriod;
}
// 如果需要跳帧,就将 Vsync 周期增加跳过的帧数
mPeriod += mPeriod * mRefreshSkipCount;
// mThread 是 DispSyncThread,更新sw model. 这个方法会唤醒 DispSync 中的 mThread 线程
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
mModelUpdated = true;
}
}
8.4 DispSync::updateModel
首先我们看这个 updateModel,updateModel 的主要工作就是给 Vsync 相关的几个时间变量赋值,并且唤醒软件 Vsync 线程。这个函数会经过多次调用,我们先看第一次调用的逻辑
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
Mutex::Autolock lock(mMutex);
// 设置 Vsync 软硬件时间的误差(也可以说是时间坐标轴的偏移)
mPhase = phase;
// 如果参考时间不相同,说明这次的 Vsync 校准后的回调还没有执行,
// 第一次调用的时 mReferenceTime 和 referenceTime 是相同的
if (mReferenceTime != referenceTime) {
for (auto& eventListener : mEventListeners) {
eventListener.mHasFired = false;
}
}
mReferenceTime = referenceTime;
if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
...
}
// 设置 Vsync 周期
mPeriod = period;
// 唤醒线程
mCond.signal();
}
到了这里,终于唤醒了之前的 threadLoop 线程了,还记得5.1中的 threadLoop 吗,当时值看了它的第一部分,现在来看它的第二部分
8.5 threadLoop的第二部分
virtual bool threadLoop() {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (true) {
...
{ ...
if (mPeriod == 0) {
// 之前会在这里陷入等待
err = mCond.wait(mMutex);
if (err != NO_ERROR) {
return false;
}
continue;
}
// 计算下一次 Vsync 的时间,这里传入了一个系统时间 now
targetTime = computeNextEventTimeLocked(now);
bool isWakeup = false;
// 如果还没有到下一次 Vsync 的时间,那么就等待,直到下一次 Vsync 的时间
if (now < targetTime) {
if (targetTime == INT64_MAX) {
err = mCond.wait(mMutex);
} else {
err = mCond.waitRelative(mMutex, targetTime - now);
}
if (err == TIMED_OUT) {
isWakeup = true;
} else if (err != NO_ERROR) {
return false;
}
}
// 拿到当前的系统时间
now = systemTime(SYSTEM_TIME_MONOTONIC);
// 校准时间不要超过1.5ns
static const nsecs_t kMaxWakeupLatency = us2ns(1500);
// 如果是没有到下次 Vsync 时间,陷入等待后,超时唤醒的,那么就进行校准
if (isWakeup) {
mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64;
mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
}
// 回调
callbackInvocations = gatherCallbackInvocationsLocked(now);
}
if (callbackInvocations.size() > 0) {
fireCallbackInvocations(callbackInvocations);
}
}
return false;
}
这个函数也比较长,再看这个函数之前,我们先看相关的类 DispSyncSource
8.5.1 DispSyncSource 的定义
前面已经说了 , 在调度器 Scheduler 中创建了一个 DispSync , 然后通过这个 DispSync 创建了一个 DispSyncSource , 这里看DispSyncSource 的定义,我们发现它实现了一个 Callback , 这个 Callback 就是 Vsync 的回调
[frameworks/native/services/surfaceflinger/Scheduler/DispSyncSource.h]
class DispSyncSource final : public VSyncSource, private DispSync::Callback
了解了 DispSyncSource 之后,我们继续看 threadLoop
threadLoop 这个函数较长,我们分开来看
- computeNextEventTimeLocked:计算下一次 Vsync 信号的时间
- gatherCallbackInvocationsLocked:这个方法获取之前设置在 DispSync 中的监听
- fireCallbackInvocations:这个方法用来分发 Vsync 到对应的监听
8.5.2 computeNextEventTimeLocked
nsecs_t computeNextEventTimeLocked(nsecs_t now) {
nsecs_t nextEventTime = INT64_MAX;
for (size_t i = 0; i < mEventListeners.size(); i++) {
// 遍历这个 mEventListeners,mEventListeners 一般有两个,一个是 SF,一个是 App
nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now);
// 如果算出的时间小于 nextEventTime,则将 nextEventTime 设置为算出的时间
if (t < nextEventTime) {
nextEventTime = t;
}
}
// 遍历完所有的 mEventListeners之后,nextEventTime 就是所有 mEventListeners 计算出来的最小值
// 也就是最接近当前时间的时间戳,这个就是下个 Vsync 信号的时间戳
return nextEventTime;
}
8.5.3 computeListenerNextEventTimeLocked
nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
// mLastEventTime 就是最后一个 SW Vsync 的目标时间,mWakeupLatency 就是上次线程醒来的延时时间
// 得到的 lastEventTime 就是最后一次 SW Vsync 的实际时间
nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
// baseTime 就是外面传进来的当前系统时间,一般来说 baseTime 肯定是大于 lastEventTime的
if (baseTime < lastEventTime) {
baseTime = lastEventTime;
}
// baseTime 减去 mReferenceTime,即减去硬件 Vsync 采样点的参考时间
// 因为我们要计算它相对于参考时间的还有多久
baseTime -= mReferenceTime;
// 计算实际偏移,记得我们一开始在创建 EventThread 的时候传递进来的偏移吗,对于 App 和 SF 传递的偏移是不一样的,
// 当时我说是为了错开 App 和 SF 的工作时间,避免造成拥挤,现在这里就用到了这个传递进来的偏移
// SW Vsync 的最终偏移就是 系统偏移mPhase + EventThread的偏移
// SF 是 SF_VSYNC_EVENT_PHASE_OFFSET_NS
// APP 是 VSYNC_EVENT_PHASE_OFFSET_NS
nsecs_t phase = mPhase + listener.mPhase;
// baseTime 再减去实际偏移的时间
baseTime -= phase;
// 经过了上面两次的相减,那么我们得到的 baseTime 就应该是最后一个硬件 Vsync 采样点
if (baseTime < 0) {
baseTime = -mPeriod;
}
// mPeriod 表示一个 Vsync 周期,baseTime / mPeriod 则表示现在到了第几个 Vsync 周期
nsecs_t numPeriods = baseTime / mPeriod;
// 计算下一个 Vsync 周期的时间,并且用它加上实际偏移的时间,就是 SW Vsync 的目标时间
nsecs_t t = (numPeriods + 1) * mPeriod + phase;
// 再将这个时间加上参考时间
// 参考时间加上相对于参考时间的时间,就是下一个 SW Vsync 的目标时间
t += mReferenceTime;
// 如果这个 SW Vsync 的目标时间距离上一个 SW Vsync 的时间小于3/5个周期,那么跳过这个 SW Vsync,
// 将它后移一个周期
if (isCloseToPeriod(t - listener.mLastEventTime)) {
t += mPeriod;
}
// 最后还要减去线程醒来的耗时
t -= mWakeupLatency;
return t;
}
bool isCloseToPeriod(nsecs_t duration) {
// Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
return duration < (3 * mPeriod) / 5;
}
computeListenerNextEventTimeLocked 这个函数非常复杂,它主要就是为了计算下一次 Vsync 的时间,所以需要考虑硬件的时间差,app 和 sf 不同的时间差,还有系统的参考时间。
8.5.4 gatherCallbackInvocationsLocked
gatherCallbackInvocationsLocked 这个函数主要就是获取之前设置在 mEventListeners 中的 callback , 这个 Callback 就是DispSyncSource , 之前我们看 DispSyncSource 的定义的时候已经说过了 , 它实现了 Callback 的回调
[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
std::vector<CallbackInvocation> callbackInvocations;
...
for (auto& eventListener : mEventListeners) {
if (t < now) {
...
ci.mCallback = eventListener.mCallback;
ci.mEventTime = t;
callbackInvocations.push_back(ci);
eventListener.mLastEventTime = t;
eventListener.mLastCallbackTime = now;
eventListener.mHasFired = true;
}
}
return callbackInvocations;
}
8.5.5 fireCallbackInvocations
[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
}
fireCallbackInvocations 这个函数则是调用之前获取到 callbacks 的 onDispSyncEvent , 其实就是调用的 DispSyncSource 的onDispSyncEvent
8.6 DispSyncSource::onDispSyncEvent
onDispSyncEvent 中的 callback 就是之前在 EventThread 构造函数中设置的 mCallback , 所以它会调用到 EventThread 中的onVSyncEvent
[frameworks/native/services/surfaceflinger/Scheduler/DispSyncSource.cpp]
void DispSyncSource::onDispSyncEvent(nsecs_t when) {
...
if (callback != nullptr) {
callback->onVSyncEvent(when);
}
}
8.7 EventThread::onVSyncEvent
[frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp]
void EventThread::onVSyncEvent(nsecs_t timestamp) {
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
mCondition.notify_all();
}
在onVSyncEvent中 , 我们发现它往 mPendingEvents 中添加了一个Event 。还记得[2.2]中的问题1吗,mPendingEvents 中的 Event 是怎么来的?
到这里,我们跟踪了系统硬件的 Vsync 信号,到 SW Vsync 模拟信号,然后 SF 中的 EventThread 处理 Vsync 的过程。
总结
关于 SurfaceFlinger 的 Vsync 机制,基本已经全部说明了。这里做一个概括。
- SurfaceFlinger 中 Vsync 信号相关的线程是如何初始化的 [见一]
- SurfaceFlinger 中 Vsync 信号的线程是如何连接信号源和回调监听的 [见二]
- SurfaceFlinger 中 MessageQueue 是如何处理 Vsync 信号的 [见三]
- Vsync 信号源的创建过程 [见四]
- Vsync 信号源的工作线程的工作原理 [见五]
- Vsync 相关控制是如何实现的 [见六和七]
- Vsync 模拟信号是如何产生的,是如何控制时间减小误差的 [见八]
- 对于 Vsync,首先它是由系统硬件发出的信号(HW-VSYNC),在 SurfaceFlinger 初始化的时候,会注册对应的硬件回调去接收这个硬件信号。
- 当 SurfaceFlinger 接收到这个硬件信号后,会将它传递到 DispSync 中,然后通过 DispSync 这个模型,分发出两个不同的软件模拟的 Vsync(SW-VSYNC)。
- 在 DispSync 不断发出模拟 Vsync 的时候,必然会存在运行时间长了导致误差逐渐增大的情况。而且将 HW-VSYNC 转换成 SW-VSYNC 的过程中,因为要经历硬件到软件的传递等一系列的 CPU 工作,所以也存在一段时间的误差,所以在 VSYNC 信号转换的过程中,和在分发 SW-SYNC 的时候需要考虑这部分的时间误差。
- 最后,SW-VSYNC 通过 MessageQueue 的消息机制分发到对应的 sf 和 app 中,然后对应的进程开始相应的工作。