SurfaceFlinger中VSYNC信号的控制同步
SurfaceFlinger(简称SF)的绘制合成过程是在VSYNC信号(即垂直同步信号)的控制下同步进行的,所以VSYNC信号可以说是SF的指挥官,它的协调同步控制对于界面绘制效率至关重要。本篇将介绍VYSNC信号在SF服务中是如何发挥这个指挥官的角色。
SurfaceFlinger中的VSYNC
SufaceFlinger的初始化是在init方法中进行的,这个方法中关于VYSNC信号有两个DispSyncSource,分别为App绘制延时源和SF合成延时源,这两个信号源基于同一个VSYNC信号模型mPrimaryDispSync,它是一个DispSync对象,DispSync是对硬件Hwc垂直信号的同步模型,那么为什么在有硬件VSYNC信号的情况下还需要一个这样的同步模型呢?实际上,这个是Android系统的一种优化策略,因为在VYSNC信号到来后,App绘制和SF合成过程如果此时同时进行,可能会竞争CPU,从而会影响绘制效率,为了避免竞争引入了VYSNC同步模型DispSync,该模型会根据需要打开硬件的VYSNC信号进行采样,然后同步VSYNC信号模型,从而为上层的绘制延时源和合成延时源提供VYSNC信号,基于该同步模型,绘制延时源和合成延时源可以分别在此基础上添加一个相位偏移量(vsyncPhaseOffsetNs和sfVsyncPhaseOffsetNs),以此错开绘制和合成在VYSNC信号到来后的执行。
void SurfaceFlinger::init() {
...
//创建合成对象HWComposer,这里会打开fb和hwc硬件设备,HWComposer代表的不一定就是实际的底层硬件设备
mHwc = new HWComposer(this,
*static_cast<HWComposer::EventHandler *>(this));
……
// start the EventThread
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true);//App绘制延时 绘制垂直同步源
mEventThread = new EventThread(vsyncSrc);//这个EventThread负责管理绘制的Vsync同步源
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, false);//SF合成延时 由于延时的不同,渲染和合成在收到真正的VSync信号之后错开执行。
mSFEventThread = new EventThread(sfVsyncSrc);//这个EventThread负责管理合成的Vsync同步源
mEventQueue.setEventThread(mSFEventThread);//这里会建立一个EventConnection,实际上就是注册成为了一个监听者,这样当有vsync信号时可以通知给MessageQueue
……
//给HWC硬件发送消息,用来控制打开关闭Vsync信号
mEventControlThread = new EventControlThread(this);
mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
……
// set initial conditions (e.g. unblank default device)
initializeDisplays();//初始化显示器,这里会重新打开Vsync信号,默认它在EventControlThread中设置是关闭的。
...
}
硬件VSYNC信号的产生
在介绍VSYNC信号如何来协调绘制和合成过程前,我们先看硬件VSYNC信号是如何产生并传递给同步模型DispSync。首先硬件的垂直信号是通过显示设备产生的,它通过HAL层的HWComposer模块将硬件垂直信号发送给SF。
//frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
HWComposer::HWComposer(
const sp<SurfaceFlinger>& flinger,
EventHandler& handler)
: mFlinger(flinger),
mFbDev(0), mHwc(0), mNumDisplays(1),
mCBContext(new cb_context),
mEventHandler(handler),
mDebugForceFakeVSync(false)
{
...
int fberr = loadFbHalModule();//打开fb设备
loadHwcModule();//打开hwc模块
if (mHwc) {//支持硬件合成
if (mHwc->registerProcs) {
mCBContext->hwc = this;
mCBContext->procs.invalidate = &hook_invalidate;
mCBContext->procs.vsync = &hook_vsync;//硬件垂直信号的回调
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))//1.1版本支持热插拔
mCBContext->procs.hotplug = &hook_hotplug;
else
mCBContext->procs.hotplug = NULL;
memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
mHwc->registerProcs(mHwc, &mCBContext->procs);//注册回调
}
// don't need a vsync thread if we have a hardware composer
needVSyncThread = false;//支持硬件合成的话就不需要软件进行模拟了
// always turn vsync off when we start
//先关闭VSYNC,后面会重新打开
eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
……
}
if (mFbDev) {//fb设备已经打开
DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
disp.connected = true;
//设置主屏幕的参数,包括显示参数包括宽度、高度,像素格式以及刷新频率
disp.width = mFbDev->width;
disp.height = mFbDev->height;
disp.format = mFbDev->format;
disp.xdpi = mFbDev->xdpi;
disp.ydpi = mFbDev->ydpi;
if (disp.refresh == 0) {
disp.refresh = nsecs_t(1e9 / mFbDev->fps);
}
if (disp.refresh == 0) {
disp.refresh = nsecs_t(1e9 / 60.0);
}
}
//需要通过软件模拟Vsync信号
if (needVSyncThread) {
// we don't have VSYNC support, we need to fake it
mVSyncThread = new VSyncThread(*this);
}
}
HWComposer对象负责SF的硬件合成,理所当然VSYNC信号也应该由其提供,在其构造方法中,会加载hwc设备模块,并将VSYNC信号的回调hook_vsync注册到hwc设备中这样硬件产生的VSYNC信号就可以回调给HWCoposer对象的hook_vsync。需要注意的是HWComposer并不一定就是底层存在的硬件设备,它也可以代表一个虚拟设备,这样VSYNC信号就是通过一个VSyncThread线程模拟硬件产生的。
//通知HWComposer垂直事件到达
void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
int64_t timestamp) {
cb_context* ctx = reinterpret_cast<cb_context*>(
const_cast<hwc_procs_t*>(procs));
ctx->hwc->vsync(disp, timestamp);
}
//VSYNC事件到达
void HWComposer::vsync(int disp, int64_t timestamp) {
if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
{
Mutex::Autolock _l(mLock);
// There have been reports of HWCs that signal several vsync events
// with the same timestamp when turning the display off and on. This
// is a bug in the HWC implementation, but filter the extra events
// out here so they don't cause havoc downstream.
if (timestamp == mLastHwVSync[disp]) {
ALOGW("Ignoring duplicate VSYNC event from HWC (t=%lld)",
timestamp);
return;
}
mLastHwVSync[disp] = timestamp;
}
……
mEventHandler.onVSyncReceived(disp, timestamp);//sf回调 将vsync消息通告给sf
}
}
在hook_vsync方法中进一步调用HWComposer的vsync方法通知VSYNC信号事件,在vsync方法中,最终是通过EventHandler的onVsyncReceived方法通知给SF的,这个EventHandler是在构造HWComposer时由SF提供的,实际上SF本身就是继承自HWComposer::EventHandler,而HWComposer::EventHandler的定义如下:
class HWComposer
{
public:
class EventHandler {
friend class HWComposer;
virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0;//vynsc消息回调
virtual void onHotplugReceived(int disp, bool connected) = 0;
protected:
virtual ~EventHandler() {}
};
}
所以VSYNC信号是通过vsync方法的mEventHandler回调onVsyncReceived通知给SF,我们看看SF是如何实现的onVsyncReceived方法
void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
bool needsHwVsync = false;
{
// Scope for the lock
Mutex::Autolock _l(mHWVsyncLock);
if (type == 0 && mPrimaryHWVsyncEnabled) {//mPrimaryHWVsyncEnabled是用来标识主屏幕对应的HWC的VSYNC功能有没有被开启
needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);//统计Vsync样本
}
}
if (needsHwVsync) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
到这一步,我们看到硬件产生的同步信号最终是交给同步模型DispSync的addResyncSample方法,根据该方法的返回值,可以根据需要控制硬件是否继续发送垂直信号,可见,硬件的垂直信号并不是持续产生的,而是同步模型在需要的时候才打开的,而什么时候需要,是由addResyncSample计算得到的。
这里我们顺便看看硬件的垂直信号是如何打开和关闭的。
//开启硬件Vsync
void SurfaceFlinger::enableHardwareVsync() {
Mutex::Autolock _l(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
mPrimaryDispSync.beginResync();
//eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
mEventControlThread->setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
//禁用硬件Vsync
void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
Mutex::Autolock _l(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
//eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
mEventControlThread->setVsyncEnabled(false);
mPrimaryDispSync.endResync();
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
mHWVsyncAvailable = false;
}
}
硬件垂直信号的开关是通过mEventControlThread的setVysncEnable方法控制的,在SF的init方法中会创建这个mEventControlThread,它是一个EventControlThread,也是一个线程。
void EventControlThread::setVsyncEnabled(bool enabled) {
Mutex::Autolock lock(mMutex);
mVsyncEnabled = enabled;
mCond.signal();
}
bool EventControlThread::threadLoop() {//EventControlThread主要用来控制硬件是否应该发送Vsync信号
Mutex::Autolock lock(mMutex);
bool vsyncEnabled = mVsyncEnabled;
//EventControlThread是在SF的Init中创建的,所以硬件Vsync信号默认是关闭的,这个在屏幕点亮后会开启
mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC,
mVsyncEnabled);
while (true) {
status_t err = mCond.wait(mMutex);//没有控制信号的时候是阻塞的
if (err != NO_ERROR) {
ALOGE("error waiting for new events: %s (%d)",
strerror(-err), err);
return false;
}
//状态发生了变化,则通知SF来控制Vysnc,HWC_DISPLAY_PRIMARY代表了主显示屏,EVENT_VSYNC说明控制的是VSync信号
if (vsyncEnabled != mVsyncEnabled) {
mFlinger->eventControl(HWC_DISPLAY_PRIMARY,
SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled);
vsyncEnabled = mVsyncEnabled;
}
}
return false;
}
在threadLoop中,它一开始默认的就通过SF的eventControl将硬件的VSYNC信号关闭,然后进入到while循环中阻塞,当通过setVsyncEnabled设置了mVsyncEnabled并唤醒线程后,根据设置的状态,通过SF的eventControl将通知hwc开关VSYNC信号。当然,在SF初始化完成后会打开硬件的VSYNC信号。这个流程我简单的介绍下。
SurfaceFlinger::initializeDisplays SurfaceFlinger::onInitializeDisplays SurfaceFlinger::onScreenAcquired SurfaceFlinger::resyncToHardwareVsync
void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
Mutex::Autolock _l(mHWVsyncLock);
if (makeAvailable) {
mHWVsyncAvailable = true;
} else if (!mHWVsyncAvailable) {
ALOGE("resyncToHardwareVsync called when HW vsync unavailable");
return;
}
const nsecs_t period =
getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);// //获得显示设备的刷新率
mPrimaryDispSync.reset();
mPrimaryDispSync.setPeriod(period);//设置DispSync模型里period为显示设备的频率
if (!mPrimaryHWVsyncEnabled) {
mPrimaryDispSync.beginResync();
//eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
//如果硬件vsync没有enable,那么就通知EventControlThread去通知硬件enable VSYNC
mEventControlThread->setVsyncEnabled(true);//打开硬件的VSYNC功能,即在屏幕点亮的时候打开
mPrimaryHWVsyncEnabled = true;
}
}
SF在init中会对显示设备进行一次初始化,这个初始化的过程最终会通过resyncToHardwareVsync根据显示设备设置同步模型的刷新频率同时也会打开硬件的VSYNC信号,以此为VSYNC同步模型做好同步的准备。
DispSync同步模型
硬件VSYNC是如何产生并交给同步模型的过程我们已经清楚了,接下来,我们看看同步模型是如何处理硬件的同步信号并为上层的监听者提供VSYNC信号的。
//Vsync的同步模型对象 默认会启动一个线程进行信号同步
DispSync::DispSync() {
mThread = new DispSyncThread();//启动同步线程,在在线程中等待垂直信号的到达
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
reset();
beginResync();
……
}
在DisySync构造方法中会默认的开启一个同步线程DispSyncThread,这个线程负责进行VYSNC信号的同步同时会将垂直同步信号发送给感兴趣的监听者(比如我们在SF的init方法中创建的两个延时源DispSyncSource)
class DispSyncThread: public Thread {
...
virtual bool threadLoop() {
status_t err;
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);//当前时间
nsecs_t nextEventTime = 0;
while (true) {
Vector<CallbackInvocation> callbackInvocations;//回调列表
nsecs_t targetTime = 0;
{ // Scope for lock
Mutex::Autolock lock(mMutex);
if (mStop) {
return false;
}
if (mPeriod == 0) {//还未设置模型频率,则等待
err = mCond.wait(mMutex);
if (err != NO_ERROR) {
ALOGE("error waiting for new events: %s (%d)",
strerror(-err), err);
return false;
}
continue;
}
nextEventTime = computeNextEventTimeLocked(now);
targetTime = nextEventTime;//触发时间
bool isWakeup = false;
if (now < targetTime) {//还未到触发时间,则等待一段时间
err = mCond.waitRelative(mMutex, targetTime - now);
if (err == TIMED_OUT) {//等待时间到达触发时间
isWakeup = true;
} else if (err != NO_ERROR) {
ALOGE("error waiting for next event: %s (%d)",
strerror(-err), err);
return false;
}
}
now = systemTime(SYSTEM_TIME_MONOTONIC);
if (isWakeup) {
mWakeupLatency = ((mWakeupLatency * 63) +
(now - targetTime)) / 64;
if (mWakeupLatency > 500000) {
// Don't correct by more than 500 us
mWakeupLatency = 500000;
}
if (traceDetailedInfo) {
ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
}
}
//收集此次应该通知的监听者
callbackInvocations = gatherCallbackInvocationsLocked(now);
}
if (callbackInvocations.size() > 0) {
//回调通知,调用注册的事件,即SF中的绘图延时和合成延时对象
fireCallbackInvocations(callbackInvocations);
}
}
return false;
}
}
DispSyncThread的逻辑很简单,它通过一个while循环,来不断的通过computeNextEventTimeLocked计算下一次VSYNC信号的时间,然后通过gatherCallbackInvocationsLocked收集要通知的监听者,最后通过fireCallbackInvocations来通知他们VSYNC信号的到达事件。这里最关键的是VYSNC信号的计算过程,同步模型是如何按照监听者的延时要求提供VSYNC信号的,它是如何保证VSYNC信号的精度和有序性的?以及模型出现误差后是如何做出调整?要回答这些问题,需要对同步模型的整个机制进行完整和全面的了解。这里为了保证文章篇幅,我们不会对同步模型做详细的描述,这里只简单的描述下即可。
前面我们知道SF在接收到硬件的VSYNC信号后通过addResyncSample方法来将信号发送给同步模型的进行样本统计。下面我们看看其实现,
bool DispSync::addResyncSample(nsecs_t timestamp) {
Mutex::Autolock lock(mMutex);
size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
mResyncSamples[idx] = timestamp;
if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
mNumResyncSamples++;
} else {
mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
}
updateModelLocked();
if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
resetErrorLocked();
}
if (runningWithoutSyncFramework) {
// If we don't have the sync framework we will never have
// addPresentFence called. This means we have no way to know whether
// or not we're synchronized with the HW vsyncs, so we just request
// that the HW vsync events be turned on whenever we need to generate
// SW vsync events.
return mThread->hasAnyEventListeners();
}
return mPeriod == 0 || mError > errorThreshold;
}
在该方法中,同步模型会收集硬件的Vsync信号的时间戳信息到mResyncSamples中,最多保存MAX_RESYNC_SAMPLES(定义为32)个硬件的VSYNC信号的时间信息,其中mNumResyncSamples和mFirstResyncSample构成了一个大小为32的VSYNC信号窗口,这个窗口最多可以包含32份硬件的VSYNC信号的时间戳信息,其中mFirstResyncSample是窗口的第一个VSYNC信号样本,而mNumResyncSamples表示已经有多少个信号样本。有了这些样本,就可以基于此来更新我们的同步模型的来使其和硬件的VSYNC信号同步。这个是通过updateModelLocked方法来完成的。
void DispSync::updateModelLocked() {
if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
nsecs_t durationSum = 0;
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
// durationSum 表示保存的所有样本(除去第一个vsync)时间间隔之后,用于后面计算 平均 mPeriod
// mResyncSamples[idx] - mResyncSamples[prev] 这个差值就是计算出两个硬件vsync样本之间的时间间隔
durationSum += mResyncSamples[idx] - mResyncSamples[prev];
}
//这个平均值就是硬件vsync产生的时间间隔
mPeriod = durationSum / (mNumResyncSamples - 1);
//下面计算出模型需要的偏移
double sampleAvgX = 0;
double sampleAvgY = 0;
double scale = 2.0 * M_PI / double(mPeriod);
//将硬件vsync的时间间隔换算成对应的度数,即刻度,这里的刻度表示每ns代表多少度
for (size_t i = 0; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sample = mResyncSamples[idx];
double samplePhase = double(sample % mPeriod) * scale;
sampleAvgX += cos(samplePhase);
sampleAvgY += sin(samplePhase);
}
//获得在x轴与y轴的偏移的平均值
sampleAvgX /= double(mNumResyncSamples);
sampleAvgY /= double(mNumResyncSamples);
//最后再通过atan2获得最终的相移值
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
//如果相移小于0 ,那么重新调整一下
if (mPhase < 0) {
mPhase += mPeriod;
}
if (traceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase);
}
// 将最新的 偏移 mPhase和 vsync时间间隔mPeriod 更新到模型当中
mThread->updateModel(mPeriod, mPhase);
}
}
updateModelLocked方法根据统计的样本来更新同步模型,只有当样本数大于等于MIN_RESYNC_SAMPLES_FOR_UPDATE(定义为3)时才进行模型的更新,当样本数大于等于3时先通过样本计算mPerid,这个值时计算方式是统计所有相邻样本的时间间隔总和到durationSum中,然后除以样本数减1就是样本的频率mPeriod。接下来计算模型的偏移,因为现在 mPeriod 算出来的是平均值,所以并不是真的硬件vsync时间间隔就是 mPeriod, 存在着误差,即有些样本信号的时间间隔大于平均值,而有些样本时间间隔小于平均值,而这些与mPriod的差值就是偏移,下面就是要算出这些平均的偏移值,计算偏移值后会将偏移值mPhase和时间间隔mPeriod更新到模型中。
void updateModel(nsecs_t period, nsecs_t phase) {
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = phase;
mCond.signal();
}
随后在同步模型线程中computeNextEventTimeLocked基于统计样本计算的mPeriod和mPhase计算下一次的VSYNC信号,接下来我们看看computeNextEventTimeLocked是如何实现的。
//得到最接近的下次VSYNC信号的时间
nsecs_t computeNextEventTimeLocked(nsecs_t now) {
nsecs_t nextEventTime = INT64_MAX;
for (size_t i = 0; i < mEventListeners.size(); i++) {
nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],now);
if (t < nextEventTime) {
nextEventTime = t;
}
}
return nextEventTime;
}
//为监听者计算下次Vsync信号事件发生时间
nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
nsecs_t ref) {
nsecs_t lastEventTime = listener.mLastEventTime;//上次的事件
if (ref < lastEventTime) {
ref = lastEventTime;
}
nsecs_t phase = mPhase + listener.mPhase;
nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase;
if (t - listener.mLastEventTime < mPeriod / 2) {
t += mPeriod;
}
return t;
}
computeNextEventTimeLocked针对所有的监听者计算下一次的VSYNC信号的发生时间,并将最接近当前时间的一次作为结果返回,而每个监听者的下一次VSYNC信号的发生时间可能是不同,因为他们可能设置了不同的偏移,因此针对每个监听者计算下一次VSYNC信号的发生时间是通过computeListenerNextEventTimeLocked完成的。
同步模型并不一定完全准确,每次计算可能都会有误差的出现,当出现误差后,则需要更新误差值,根据误差值来判断是否需要开启硬件VSYNC重新添加样本到同步模型中进行计算。这个过程是在SF的postCompostion中进行的。
void SurfaceFlinger::postComposition()
{
...
const HWComposer& hwc = getHwComposer();
sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
if (mPrimaryDispSync.addPresentFence(presentFence)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
...
}
在postComposition中先拿到当前设备的Fence,然后通过addPresentFence计算同步模型的误差值,根据误差值来决定是否需要启用硬件VSYNC。
通知监听者
//收集回调,根据回调的延时偏量计算是否要触发回调
Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
Vector<CallbackInvocation> callbackInvocations;
nsecs_t ref = now - mPeriod;
for (size_t i = 0; i < mEventListeners.size(); i++) {
nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
ref);
if (t < now) {//小于当前时间的都是需要通知的监听者
CallbackInvocation ci;
ci.mCallback = mEventListeners[i].mCallback;
ci.mEventTime = t;
callbackInvocations.push(ci);
mEventListeners.editItemAt(i).mLastEventTime = t;
}
}
return callbackInvocations;
}
计算完下次要触发VSYNC信号的时间后,可能需要等待一段时间,因为当前时间还未到达最近的触发事件,当到达触发的时间后,同步模型线程会通过gatherCallbackInvocationsLocked收集需要进行通知的监听者,如果监听者的下次VSYNC信号发生时间已经小于本次VSYNC信号的触发时间,则说明监听者需要进行通知了,将其添加到集合中等待回调。
//回调对同步模型的VSync信号感兴趣的监听者
void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
}
最后通过监听者的添加的回调通知其VSYNC信号已经到达。
绘图延时源和合成延时源(DispSyncSource)
在SF的init方法中我们知道SF创建了两个延时源对象DispSyncSource,他们基于同步模型来处理VSYNC信号,这两个不同的延时源通过两个不同的EventThread来管理,他们分别为mEventThread和mSFEventThread,这里的EventThread是为了延时源方便管理VSYNC信号,比如对于绘图延时源,它的监听者就有大名鼎鼎的Choreographer,上层App的绘制过程正是在Choreographer的协调下同步进行的,而Choreographer正是注册到绘图延时源的EventThread中以此来监听VSYNC信号,而SF是注册到合成延时源的EventThread中。
下面我们看看由同步模型传递给延时源的VSYNC信号是如何使用传递给需要的监听者的。
class DispSyncSource : public VSyncSource, private DispSync::Callback {//VsyncSource定义在EventThread.h
public:
//DispSync是Vsync信号的模型对象
DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync) :
mValue(0),
mPhaseOffset(phaseOffset),//距离Vsync信号的延时偏移
mTraceVsync(traceVsync),
mDispSync(dispSync) {}
virtual ~DispSyncSource() {}
virtual void setVSyncEnabled(bool enable) {
// Do NOT lock the mutex here so as to avoid any mutex ordering issues
// with locking it in the onDispSyncEvent callback.
if (enable) {//如果启用Vsync
status_t err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));//添加Vsync信号的事件回调
if (err != NO_ERROR) {
ALOGE("error registering vsync callback: %s (%d)",
strerror(-err), err);
}
ATRACE_INT("VsyncOn", 1);
} else {
status_t err = mDispSync->removeEventListener(
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error unregistering vsync callback: %s (%d)",
strerror(-err), err);
}
ATRACE_INT("VsyncOn", 0);
}
}
virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
Mutex::Autolock lock(mMutex);
mCallback = callback;
}
private:
//实现了DispSync的回调接口,这个回调是在DispSync中通过fireCallbackInvocations来调用的
virtual void onDispSyncEvent(nsecs_t when) {
sp<VSyncSource::Callback> callback;
{
Mutex::Autolock lock(mMutex);
callback = mCallback;
if (mTraceVsync) {
mValue = (mValue + 1) % 2;
ATRACE_INT("VSYNC", mValue);
}
}
if (callback != NULL) {
callback->onVSyncEvent(when);//调用设置的回调
}
}
int mValue;
const nsecs_t mPhaseOffset;
const bool mTraceVsync;
DispSync* mDispSync;
sp<VSyncSource::Callback> mCallback;
Mutex mMutex;
};
延时源通过同步模型DispSync来构造,同步模型通过接口onDispSyncEvent上报给延时源VSYNC信号,并通过延时源设置的回调callback的onVSyncEvent方法将VSYNC信号的到达事件发送给设置者(事实上就是EventThread)。同时,延时源可以通过setVSyncEnabled方法来控制是否监听来自于同步模型的VSYNC信号。
EventThread
EventThread顾明思议,它实际上也是一个Thread,它创建的时候就会启动该线程.我们看看它的线程回调
//frameworks/native/services/surfaceflinger/EventThread.cpp
bool EventThread::threadLoop() {
DisplayEventReceiver::Event event;
Vector< sp<EventThread::Connection> > signalConnections;
signalConnections = waitForEvent(&event);//阻塞中,等待VYSNC信号通知
// dispatch events to listeners...
const size_t count = signalConnections.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Connection>& conn(signalConnections[i]);
// now see if we still need to report this event
status_t err = conn->postEvent(event);//post事件
if (err == -EAGAIN || err == -EWOULDBLOCK) {
// The destination doesn't accept events anymore, it's probably
// full. For now, we just drop the events on the floor.
// FIXME: Note that some events cannot be dropped and would have
// to be re-sent later.
// Right-now we don't have the ability to do this.
ALOGW("EventThread: dropping event (%08x) for connection %p",
event.header.type, conn.get());
} else if (err < 0) {
// handle any other error on the pipe as fatal. the only
// reasonable thing to do is to clean-up this connection.
// The most common error we'll get here is -EPIPE.
removeDisplayEventConnection(signalConnections[i]);
}
}
return true;
}
线程回调方法首先通过waitForEvent等待VSYNC信号的通知,同时获取到需要通知的Connection,这里的Connection就是延时源VSYNC信号的监听者,随后通过Connection的postEvent方法将事件发送给监听者,所以EventThread最核心的内容应该是在waitForEvent中进行处理的。
//当接收到vsync信号时接收到 或者至少有一个连接对VSYNC信号感兴趣此方法返回给调用者
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
DisplayEventReceiver::Event* event)
{
Mutex::Autolock _l(mLock);
Vector< sp<EventThread::Connection> > signalConnections;
do {
bool eventPending = false;
bool waitForVSync = false;
size_t vsyncCount = 0;
nsecs_t timestamp = 0;
//关键点1 垂直信号事件
for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
timestamp = mVSyncEvent[i].header.timestamp;
if (timestamp) {
// we have a vsync event to dispatch
*event = mVSyncEvent[i];//垂直事件到来
mVSyncEvent[i].header.timestamp = 0;
vsyncCount = mVSyncEvent[i].vsync.count;
break;
}
}
if (!timestamp) {
// no vsync event, see if there are some other event
eventPending = !mPendingEvents.isEmpty();
if (eventPending) {
// we have some other event to dispatch
*event = mPendingEvents[0];//其他的事件
mPendingEvents.removeAt(0);
}
}
// find out connections waiting for events
//关键点2 查看是否有对VSYNC信号感兴趣的连接
size_t count = mDisplayEventConnections.size();
for (size_t i=0 ; i<count ; i++) {
sp<Connection> connection(mDisplayEventConnections[i].promote());//取得连接
if (connection != NULL) {
bool added = false;
if (connection->count >= 0) {//关于这个显示屏垂直信号的客户端个数大于等于0
// we need vsync events because at least
// one connection is waiting for it
waitForVSync = true;//至少有一个连接在等待VSync事件
if (timestamp) {
// we consume the event only if it's time
// (ie: we received a vsync event)
if (connection->count == 0) {
// fired this time around
connection->count = -1;
signalConnections.add(connection);
added = true;
} else if (connection->count == 1 ||
(vsyncCount % connection->count) == 0) {
// continuous event, and time to report it
signalConnections.add(connection);
added = true;
}
}
}
if (eventPending && !timestamp && !added) {
// we don't have a vsync event to process
// (timestamp==0), but we have some pending
// messages.
signalConnections.add(connection);
}
} else {
// we couldn't promote this reference, the connection has
// died, so clean-up!
mDisplayEventConnections.removeAt(i);
--i; --count;
}
}
// Here we figure out if we need to enable or disable vsyncs
//关键点3 根据需要开启和关闭VSYNC信号,这里的开启和关闭是指注册回调到延时源以及从同步源移除或者添加监听
if (timestamp && !waitForVSync) {//收到了一个垂直信号当时没有客户端感兴趣
// we received a VSYNC but we have no clients
// don't report it, and disable VSYNC events
disableVSyncLocked();
} else if (!timestamp && waitForVSync) {
// we have at least one client, so we want vsync enabled
// (TODO: this function is called right after we finish
// notifying clients of a vsync, so this call will be made
// at the vsync rate, e.g. 60fps. If we can accurately
// track the current state we could avoid making this call
// so often.)
enableVSyncLocked();//启用Vsync
}
// note: !timestamp implies signalConnections.isEmpty(), because we
// don't populate signalConnections if there's no vsync pending
if (!timestamp && !eventPending) {
// wait for something to happen
if (waitForVSync) {//如果VSYNC信号未到达,但有管兴趣的连接等待VSYNC,则等待VSYNC信号,线程阻塞等待唤醒
// This is where we spend most of our time, waiting
// for vsync events and new client registrations.
//
// If the screen is off, we can't use h/w vsync, so we
// use a 16ms timeout instead. It doesn't need to be
// precise, we just need to keep feeding our clients.
//
// We don't want to stall if there's a driver bug, so we
// use a (long) timeout when waiting for h/w vsync, and
// generate fake events when necessary.
bool softwareSync = mUseSoftwareVSync;
nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
if (!softwareSync) {
ALOGW("Timed out waiting for hw vsync; faking it");
}
// how do we decide which display id the fake
// vsync came from ?
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;//消息类型
mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
mVSyncEvent[0].vsync.count++;
}
} else {//没有对VSync信号感兴趣的,只是让线程阻塞
// Nobody is interested in vsync, so we just want to sleep.
// h/w vsync should be disabled, so this will wait until we
// get a new connection, or an existing connection becomes
// interested in receiving vsync again.
mCondition.wait(mLock);
}
}
} while (signalConnections.isEmpty());
// here we're guaranteed to have a timestamp and some connections to signal
// (The connections might have dropped out of mDisplayEventConnections
// while we were asleep, but we'll still have strong references to them.)
return signalConnections;
}
垂直事件到达后会将其保存在参数event中,如果timestamp是0,表示没有VSYNC信号到达。mDisplayEventConnections中保存了已经注册的监听者。如果此时connection的count大于等于0,则表示有监听者对VSYNC信号感兴趣,同时置waitForVSync为true,同时将该监听者添加到signalConnections集合中。这里connection的count值的含义如下:
- count >= 1 : continuous event. count is the vsync rate 如果在大于等于1,表示会持续接收vsync event
- count == 0 : one-shot event that has not fired 表示只接收一次
- count ==-1 : one-shot event that fired this round / disabled 等于-1,表示不能再接收vsync事件了
当满足timestamp && !waitForVSync时表示VSYNC信号已经到达,但是此时没有感兴趣的监听者,所以此时不需要再接收VSYNC信号了,通过disableVSyncLocked移除对同步模型的VSYNC信号的监听。
当!timestamp && waitForVSync满足时则说明有感兴趣的监听者,但VSYNC信号还未达到,这时候需要使用enableVSyncLocked将延时源添加到同步模型的监听者集合中。
如果!timestamp && !eventPending则表示没有VSYNC信号到达,且没有其他等待的事件,但此时如果waitForVSync为true,则要等待VSYNC信号,但此时不会无限等待下去,而是有一个超时时间。
当VSYNC信号到达后,通过EventThread设置的callback来接收来自同步模型的VSYNC信号,这个回调就是EventThread的onVSyncEvent方法
void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;//准备好发送的消息
mVSyncEvent[0].header.id = 0;
mVSyncEvent[0].header.timestamp = timestamp;
mVSyncEvent[0].vsync.count++;
mCondition.broadcast();//广播解除threadloop的阻塞
}
在onVSyncEvent方法中,会解除在waitForEvent中等待VSYNC信号的阻塞状态。从而退出while循环,通知相应的监听者VSYNC信号的到达。
总结
至此,我们就将VSYNC信号在SF中的传递和控制过程介绍完了,我们简单总结下整个传递过程:
- 硬件或者由软件模拟触发VSYNC信号,通知给SF
- SF接收到硬件的VSYNC信号后将其添加到同步模型DispSync的样本数组中进行统计和计算模型的偏移和周期
- 同步模型根据计算的偏移和周期计算下次VSYNC信号发生时间,并通知监听者VSYNC信号到达的事件
- 同步模型的VSYNC信号传递给延时源,延时源通过EventThread来管理VSYNC信号的收发