上一篇文章分析到了SurfaceFlinger进程接收到来自硬件的Vsync的处理流程,主要是在EventThread内部线程的threadMain函数中,这个函数中主要分析了对接收到的事件的处理,并没有分析它的来源,导致遗留了两个问题:1.mPendingEvents里的事件是怎么来的,2.setVSyncEnabled函数的具体作用
setVSyncEnabled函数的作用也分析了一部分,主要是添加或者移除监听器,它监听的是什么?是来自DispSync内部线程DispSyncThread捕捉到的Vsync,我们这篇文章要分析的是DispSyncThread对捕捉到Vsync信号之后的内部处理,DispSyncThread的threadLoop中会开启一个死循环,在捕捉到Vsync之后会通知监听器,所以本篇文章的分析分为两步:1.DispSyncThread如何捕捉Vsync,2.捕捉到Vsync之后如何通知监听器
首先来看步骤1,想要知道DispSyncThread如何捕捉Vsync必须先知道Vsync如何发送的,如何到DispSyncThread中的:
先来看看SurfaceFlinger的定义:
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback
SurfaceFlinger继承BnSurfaceComposer,作为Binder Bn端,另外还实现了一个Callback,HWC2::ComposerCallback
我们来看看这个Callback定义:
//HWC2.h
class ComposerCallback {
public:
virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
Connection connection) = 0;
virtual void onRefreshReceived(int32_t sequenceId,
hwc2_display_t display) = 0;
virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
int64_t timestamp) = 0;
virtual ~ComposerCallback() = default;
};
注意看它定义的三个回调函数,分别对应三种事件类型,热插拔,屏幕刷新,Vsync信号,surfaceflinger实现了这三个回调方法,并且在init函数中通过如下代码最终将自己注册到了Hwc2的ComposerCallbackBridge中
mCompositionEngine->getHwComposer().registerCallback(this,
getBE().mComposerSequenceId);
来看看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();
}
我们可以看到ComposerCallbackBridge中有三个函数依次对应ComposerCallback中的三个回调函数,刚好ComposerCallbackBridge的某个函数被调用之后就会调到surfaceFlinger对应的某个回调函数中
而ComposerCallbackBridge的几个函数则是通过hal从硬件调用过来的,看不了代码也没必要进行分析,大概调用栈为:
hardware->BHwBinder->BnHwComposerCallback::onTransact->BnHwComposerCallback::_hidl_onVsync->ComposerCallbackBridge::onVsync->SurfaceFlinger::onVsyncReceived
所以SurfaceFlinger的onVsyncReceived函数最终接收的就是来自硬件的Vsync信号
继续来看看SurfaceFlinger的onVsyncReceived函数具体实现:
onVsyncReceived
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
int64_t timestamp) {
......
bool periodChanged = false;
mScheduler->addResyncSample(timestamp, &periodChanged);
if (periodChanged) {
mVsyncModulator.onRefreshRateChangeDetected();
}
}
接着调用mScheduler的addResyncSample函数
void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
bool needsHwVsync = false;
*periodChanged = false;
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
}
}
.....
}
mPrimaryDispSync类型为DispSync,DispSync内部有一个线程DispSyncThread
DispSync.addResyncSample
bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) {
......
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
......
}
这个线程是surfaceFlinger进程中处理Vsync的源头,调用DispSyncThread的updateModel函数
mThread->updateModel
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
......
mCond.signal();
}
通过C++的条件变量唤醒处于wait状态的线程
到这里第一步DispSyncThread捕捉Vsync分析完了,来看第二步,DispSyncThread的threadLoop函数
DispSyncThread.threadLoop
virtual bool threadLoop() {
status_t err;
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (true) {
....
std::vector<CallbackInvocation> callbackInvocations;
.....
if (mPeriod == 0) {
err = mCond.wait(mMutex);
if (err != NO_ERROR) {
return false;
}
continue;
}
targetTime = computeNextEventTimeLocked(now);
bool isWakeup = false;
if (now < targetTime) {
if (targetTime == INT64_MAX) {
err = mCond.wait(mMutex);
} else {
err = mCond.waitRelative(mMutex, targetTime - now);
}
.....
now = systemTime(SYSTEM_TIME_MONOTONIC);
......
callbackInvocations = gatherCallbackInvocationsLocked(now);
}
if (callbackInvocations.size() > 0) {
fireCallbackInvocations(callbackInvocations);
}
}
return false;
}
DispSyncThread在启动时就会开启死循环,当没有事件时调用mCond.wait陷入wait状态,其实DispSyncThread和上一篇文章分析的EventThread内部线程是同样的工作机制,同样的死循环,依靠唤醒/等待完成所有工作,
DispSyncThread被唤醒之后,首先得到下一个Vsync信号的时间,如果大于当前时间now,则会继续等待,否则会调用gatherCallbackInvocationsLocked函数从mEventListeners中获取处理Vsync时间小于当前时间的监听器,获取之后再调用fireCallbackInvocations函数,此函数中再调用所以监听器的onDispSyncEvent函数
onDispSyncEvent这个回调函数我们上一篇文章分析过,是由DispSyncSource实现的,
DispSyncSource.onDispSyncEvent
void DispSyncSource::onDispSyncEvent(nsecs_t when) {
VSyncSource::Callback* callback;
{
std::lock_guard lock(mCallbackMutex);
callback = mCallback;
....
if (callback != nullptr) {
callback->onVSyncEvent(when);
}
}
DispSyncSource的onDispSyncEvent函数中又调用了回调函数onVSyncEvent,而onVSyncEvent这个回调则是由EventThread实现的,是在EventThread的构造函数中注册到DispSyncSource的
mVSyncSource->setCallback(this);
继续看EventThread的onVSyncEvent函数
EventThread.onVSyncEvent
void EventThread::onVSyncEvent(nsecs_t timestamp) {
......
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
mCondition.notify_all();
}
终于看到我们遗留的第一个问题了:mPendingEvents的事件是怎么来的?mPendingEvents中的事件是在DispSyncThread收到Vsync之后填充的,这里调用makeVSync构造了一个事件
makeVSync
DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
uint32_t count) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
event.vsync.count = count;
return event;
}
构造了一个DisplayEventReceiver::Event类型事件,类型为DISPLAY_EVENT_VSYNC,还有displayId,时间戳以及接收到的次数
我们可以发现VSync并不是一个实际的信号,更像是一条通知,EventThread收到通知之后自己构造一个Event,然后发送出去
VSync类型事件构造好了之后通过条件变量唤醒陷入wait状态的EventThread内部线程,这个线程的作用在前一篇文章就分析了,它会从mPendingEvents中拿到最头部事件调用dispatchEvent将事件分发给感兴趣的监听者,感兴趣的监听者即是向EventThread请求了Vsync的EventThreadConnection
dispatchEvent的实现很简单,最终就是通过gui::BitTube的sendObjects函数向mSendFd中写入数据,另一端监听了mReceiveFd的进程就能够收到消息,知道Vsync到来了,然后完成绘制工作
而我们遗留的问题2:setVSyncEnabled的作用就是开启或关闭对DispSyncThread发送的Vsync通知的监听,当我们不需要绘制UI时例如灭屏就会调用setVSyncEnabled(false)关闭监听,这样EventThread中就收不到任何Vsync事件,并通过条件变量的wait函数陷入等待
到这里通过四篇文章就大概分析完了VSync机制的工作原理