本文旨在研究這個話題-- vsync是如何有序控制sf合成和app繪制的節(jié)奏?
應(yīng)用需要等VSYNC-app脈沖來進(jìn)行繪制,繪制完后又需要等VSYNC-sf脈沖在surfaceflinger里面進(jìn)行合成,VSYNC-app和VSYNC-sf的觸發(fā)是這篇文章的重點,先來回顧下繪制->合成的整個鏈路,大致如下:{Pid: UI Thread}Choreographer#doFrame -> (Input、animation、traversal)-> draw -> {Pid: Renderthread} DrawFrames -> syncFrameState -> flush commands -> queueBuffer -> acquireNextBufferLocked -> {Pid: SF} setTransactionState -> queueTransaction -> setTransactionFlags。
Call到surfaceflinger的setTransactionFlags設(shè)置有更新的transaction,來表示應(yīng)用要更新幀,先看下這個Func:
void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
const sp<IBinder>& applyToken, FrameHint frameHint) {
modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
if (const bool scheduled = mTransactionFlags.fetch_or(mask) & mask; !scheduled) {
scheduleCommit(frameHint);
}
}
modulateVsync: 主要是更新CallbackRepeater類里面mWorkDuration 和 mReadyDuration這兩個值,這兩個值參與了vsync的計算。該func流程是根據(jù)schedule的類型決定VsyncConfigType 是選early,earlyGPU,還是late,然后取對應(yīng)的duration賦值給mWorkDuration、mReadyDuration。以60hz為例,系統(tǒng)設(shè)置的early、GL early和late如下,那如果VsyncConfigType 選擇為late,就將app duration 賦值給mWorkDuration,SF duration賦值給mReadyDuration
app phase: 1000000 ns SF phase: 1000000 ns
app duration: 16666666 ns SF duration: 15666666 ns
early app phase: 1000000 ns early SF phase: 1000000 ns
early app duration: 16666666 ns early SF duration: 15666666 ns
GL early app phase: 1000000 ns GL early SF phase: 1000000 ns
GL early app duration: 16666666 ns GL early SF duration: 15666666 ns
HWC min duration: 0 ns
present offset: 0 ns VSYNC period: 16666666 ns
void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
nsecs_t vsyncPeriod) {
mScheduler->setDuration(mAppConnectionHandle,
/*workDuration=*/config.appWorkDuration,
/*readyDuration=*/config.sfWorkDuration);
mScheduler->setDuration(mSfConnectionHandle,
/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
/*readyDuration=*/config.sfWorkDuration);
mScheduler->setDuration(config.sfWorkDuration);
}
void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
std::lock_guard lock(mVsyncMutex);
mWorkDuration = workDuration;
mReadyDuration = readyDuration;
// If we're not enabled, we don't need to mess with the listeners
if (!mEnabled) {
return;
}
mCallbackRepeater->start(mWorkDuration, mReadyDuration);
}
mTransactionFlags:SF類的一個全局變量,通過setTransactionFlags 來增加flag,通過clearTransactionFlags來去掉對應(yīng)的flag,flag類型如下:
enum {
eTransactionNeeded = 0x01,
eTraversalNeeded = 0x02, // 1和2表示這幀有l(wèi)ayer狀態(tài)的變化,比如:layerstack,size,alpha等
eDisplayTransactionNeeded = 0x04, // 表示有display狀態(tài)的變化,比如:DisplaySize,DestoryDisplay
eTransformHintUpdateNeeded = 0x08,
eTransactionFlushNeeded = 0x10, //表示需要合成這些變化的狀態(tài)
eTransactionMask = 0x1f,
};
回到setTransactionFlags,若mTransactionFlags沒有
eTransactionFlushNeeded flag,則將mTransactionFlags加上eTransactionFlushNeeded,然后執(zhí)行scheduleCommit,如圖1所示。若mTransactionFlags本身有了eTransactionFlushNeeded則不會執(zhí)行scheduleCommit,這種情況一般是這幀有mPendingTransactionQueues或者mTransactionQueue不為空,如圖2所示。


看下scheduleCommit,走到MessageQueue::scheduleFrame里面,如下:
void MessageQueue::scheduleFrame() {
ATRACE_CALL();
{
std::lock_guard lock(mInjector.mutex);
if (CC_UNLIKELY(mInjector.connection)) {
ALOGD("%s while injecting VSYNC", __FUNCTION__);
mInjector.connection->requestNextVsync();
return;
}
}
std::lock_guard lock(mVsync.mutex);
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
.earliestVsync = mVsync.lastCallbackTime.count()});
}
主要來看下schedule 這個func,schedule是計算vsync時間戳的入口,如下:
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
return std::nullopt;
}
return mDispatch.get().schedule(mToken, scheduleTiming);
}
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
ScheduleResult result;
{
std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
return result;
auto& callback = it->second;
auto const now = mTimeKeeper->now();
/* If the timer thread will run soon, we'll apply this work update via the callback
* timer recalculation to avoid cancelling a callback that is about to fire. */
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
callback->addPendingWorkloadUpdate(scheduleTiming);
return getExpectedCallbackTime(mTracker, now, scheduleTiming);
}
result = callback->schedule(scheduleTiming, mTracker, now);
if (!result.has_value()) {
return result;
}
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
return result;
}
這里有個mToken來表示具體哪種類型的回調(diào),在初始化時注冊了3種類型的回調(diào),分別為sf, app, appSf
// 注冊sf的回調(diào), callback為MessageQueue::vsyncCallback
void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
frametimeline::TokenManager& tokenManager,
std::chrono::nanoseconds workDuration) {
setDuration(workDuration);
mVsync.tokenManager = &tokenManager;
mVsync.registration = std::make_unique<
scheduler::VSyncCallbackRegistration>(dispatch,
std::bind(&MessageQueue::vsyncCallback, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3),
"sf");
}
/* 注冊app和appSf的回調(diào),由createConnection發(fā)起,mName分別為app和appSf,callback為
DispSyncSource::onVsyncCallback */
CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
std::chrono::nanoseconds notBefore)
: mName(name),
mCallback(cb),
mRegistration(dispatch,
std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3),
mName),
mStarted(false),
mWorkDuration(workDuration),
mReadyDuration(readyDuration),
mLastCallTime(notBefore) {}
//分別將callbackName和callback帶進(jìn)來執(zhí)行 registerCallback
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
VSyncDispatch::Callback callback,
std::string callbackName)
: mDispatch(dispatch),
mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
mValidToken(true) {}
//sf, app, appSf分別創(chuàng)建了VSyncDispatchTimerQueueEntry,mCallbacks size為3
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
Callback callback, std::string callbackName) {
std::lock_guard lock(mMutex);
return CallbackToken{
mCallbacks
.emplace(++mCallbackToken,
std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
std::move(callback),
mMinVsyncDistance))
.first->first};
}
回到schedule里面,此時mToken表示sf的callback,mIntendedWakeupTime表示這幀預(yù)期喚醒時間,now 小于 mIntendedWakeupTime,所以走到了VSyncDispatchTimerQueueEntry::schedule,如下:
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
return getExpectedCallbackTime(nextVsyncTime, timing);
}
bool const alreadyDispatchedForVsync = mLastDispatchTime &&
((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
(*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
if (alreadyDispatchedForVsync) {
nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
}
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
return getExpectedCallbackTime(nextVsyncTime, timing);
}
這個Func的一堆時間戳看的頭暈眼花,可以嘗試把這些時間戳打在trace上一幀幀查看。真正計算vsync時間戳的Func是nextAnticipatedVSyncTimeFrom,在講這個函數(shù)前需要介紹下vsync計算模型,了解了這個模型,后面再看到nextAnticipatedVSyncTimeFrom就可以只用看輸入的時間戳以及對應(yīng)的輸出。
Vsync計算模型
從切幀的角度來看這個模型,當(dāng)系統(tǒng)發(fā)生切幀時,會通過resyncToHardwareVsync->setVsyncPeriod->
setVsyncEnabled 來打開HW Vsync校準(zhǔn)SW Vsync到預(yù)期的周期,如下
void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
...
if (display->setDesiredActiveMode(info)) {
// 讓sf強行合成一幀
scheduleComposite(FrameHint::kNone);
// Start receiving vsync samples now, so that we can detect a period
// switch.
// 開HW Vsync和設(shè)置mPeriodTransitioningTo
mScheduler->resyncToHardwareVsync(true, info.mode->getFps());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
// 更新mode對應(yīng)的duration、phase
updatePhaseConfiguration(info.mode->getFps());
mScheduler->setModeChangePending(true);
}
...
}
void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate, bool force_resync) {
{
...
setVsyncPeriod(refreshRate.getPeriodNsecs(), force_resync);
}
void Scheduler::setVsyncPeriod(nsecs_t period, bool force_resync) {
if (period <= 0) return;
std::lock_guard<std::mutex> lock(mHWVsyncLock);
mVsyncSchedule->getController().startPeriodTransition(period);
/* mPrimaryHWVsyncEnabled 代表HW Vsync 是否enable,若關(guān)閉或者強行打開則使能,
告訴display驅(qū)動需要校準(zhǔn)*/
if (!mPrimaryHWVsyncEnabled || force_resync) {
mVsyncSchedule->getTracker().resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
void VSyncReactor::startPeriodTransition(nsecs_t period) {
ATRACE_INT64("VSR-setPeriod", period);
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
// 如果模型里面的周期與要切換的周期一致,則不需要更多的采樣,下一步就是關(guān)閉HW Vsync
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
} else {
startPeriodTransitionInternal(period);
}
}
//不一致則會把把period設(shè)給mPeriodTransitioningTo,然后需要采樣
void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
ATRACE_CALL();
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
setIgnorePresentFencesInternal(true);
}
切幀時會設(shè)一次scheduleComposite讓sf強行合成一次,這一次合成的目的是在setActiveModeInHwcIfNeeded 時把預(yù)期的mode傳給HWC,這樣讓display驅(qū)動切到預(yù)期的mode后就能往上報時間戳來校準(zhǔn)SW Vsync。試想如果不強行合成一幀,如果下一幀沒有應(yīng)用transaction的變化,sf不合成,就無法將正確的mode傳遞給驅(qū)動。
void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
...
// 將預(yù)期的mode傳遞給驅(qū)動
const auto status = FTL_FAKE_GUARD(kMainThreadContext,
display->initiateModeChange(*desiredActiveMode,
constraints, &outTimeline));
...
}
驅(qū)動切幀時會上報時間戳給到surfaceflinger進(jìn)行校準(zhǔn),如下:
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
...
//驅(qū)動會傳timestamp和vsyncPeriod過來校準(zhǔn)
mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
...
}
void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) {
bool needsHwVsync = false;
*periodFlushed = false;
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
// 加入HW Vsync Timestamp進(jìn)行校準(zhǔn)
needsHwVsync =
mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
periodFlushed);
}
}
if (needsHwVsync) {
// 如果還需要采樣,則繼續(xù)打開HW Vsync
enableHardwareVsync();
} else {
// 如果不需要采樣,則關(guān)閉
disableHardwareVsync(false);
}
}
bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) {
assert(periodFlushed);
std::lock_guard lock(mMutex);
/* 首先判斷驅(qū)動的周期和設(shè)的mPeriodTransitioningTo是否在誤差范圍內(nèi),如果在誤差范圍內(nèi)則表示
驅(qū)動已經(jīng)切到指定的周期 */
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
ATRACE_NAME("VSR: period confirmed");
if (mPeriodTransitioningTo) {
// 設(shè)置mIdealPeriod為預(yù)期的周期
mTracker.setPeriod(*mPeriodTransitioningTo);
*periodFlushed = true;
}
if (mLastHwVsync) {
mTracker.addVsyncTimestamp(*mLastHwVsync);
}
// addVsyncTimestamp 是vsync模型的核心,將驅(qū)動傳來的時間戳加進(jìn)來校準(zhǔn)
mTracker.addVsyncTimestamp(timestamp);
/* 將 mPeriodConfirmationInProgress設(shè)為false,表示已經(jīng)確認(rèn)驅(qū)動的周期是正確的,接下來
不會再走periodConfirmed了 */
endPeriodTransition();
// 如果收集到的mTimestamps size小于6,則繼續(xù)進(jìn)行采樣
mMoreSamplesNeeded = mTracker.needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
ATRACE_NAME("VSR: still confirming period");
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
} else {
ATRACE_NAME("VSR: adding sample");
*periodFlushed = false;
// 接下來當(dāng)不在進(jìn)行periodConfirmed 時會走”adding sample“ 繼續(xù)添加時間戳
mTracker.addVsyncTimestamp(timestamp);
// 如果收集到的mTimestamps size小于6,則繼續(xù)進(jìn)行采樣
mMoreSamplesNeeded = mTracker.needsMoreSamples();
}
if (!mMoreSamplesNeeded) {
setIgnorePresentFencesInternal(false);
}
return mMoreSamplesNeeded;
}
可以看到addHwVsyncTimestamp 是先判斷驅(qū)動的周期是否成功切換為預(yù)期值,如果成功,則走"adding sample" 來添加時間戳,如果失敗,則需要繼續(xù)走periodConfirmed判斷驅(qū)動的周期。
Vsync模型的核心在addVsyncTimestamp體現(xiàn),Google也是很友好的寫了注釋來解釋這個模型,其實就是簡單的一元線性回歸,用來確定2個變量之間存在定量關(guān)系的統(tǒng)計方法,將兩個變量用一條直線近似表示。
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { std::lock_guard lock(mMutex);
/* validate 來判斷驅(qū)動前后兩幀的時間戳差值與mIdealPeriod 的誤差。
正常情況下驅(qū)動上傳的前后兩幀時間戳的差應(yīng)該與 mIdealPeriod相差不大,如果誤差較大,則可能驅(qū)動
產(chǎn)生的時間戳有問題 */
if (!validate(timestamp)) {
if (mTimestamps.size() < kMinimumSamplesForPrediction) {
mTimestamps.push_back(timestamp);
clearTimestamps();
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
} else {
mKnownTimestamp = timestamp;
}
return false;
}
// 如果時間戳有效,則push到mTimestamps
if (mTimestamps.size() != kHistorySize) {
mTimestamps.push_back(timestamp);
mLastTimestampIndex = next(mLastTimestampIndex);
} else {
mLastTimestampIndex = next(mLastTimestampIndex);
mTimestamps[mLastTimestampIndex] = timestamp;
}
const size_t numSamples = mTimestamps.size();
if (numSamples < kMinimumSamplesForPrediction) {
//如果有效的時間戳小于6,則需要繼續(xù)增加樣本,提前return
mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
return true;
}
// This is a 'simple linear regression' calculation of Y over X, with Y being the
// vsync timestamps, and X being the ordinal of vsync count.
// The calculated slope is the vsync period.
// Formula for reference:
// Sigma_i: means sum over all timestamps.
// mean(variable): statistical mean of variable.
// X: snapped ordinal of the timestamp
// Y: vsync timestamp
//
// Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )
// slope = -------------------------------------------
// Sigma_i ( X_i - mean(X) ) ^ 2
//
// intercept = mean(Y) - slope * mean(X)
//
/* 當(dāng)mTimestamps滿6個時,使用一元線性回歸,將采集到的mTimestamps時間戳當(dāng)成Y,序號當(dāng)作X
計算slope 和 intercept 來滿足 Y = slope * X + intercept 的線性關(guān)系
slope 為直線的斜率即 vsync周期, intercept 為截距 */
it->second = {anticipatedPeriod, intercept};
ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
anticipatedPeriod, intercept);
return true;
}
可以看到addVsyncTimestamp先通過validate判斷驅(qū)動前后兩幀的時間戳差值與mIdealPeriod 的誤差,若為有效的時間戳則加入到mTimestamps里面,收集滿6個則采用一元線性回歸擬合時間戳與序列號成直線關(guān)系,為后面預(yù)估vsync時間戳做準(zhǔn)備。按照Google的注釋,我們用Matlab畫個圖,我們先收集6個有效的時間戳,如下圖:






Matlab程序如下:
x=[1,2,3,4,5,6];
y=[2778939.392000, 2778947.684000, 2778955.976000, 2778964.268000, 2778972.560000, 2778980.853000];
figure
plot(x,y,'r*') %作散點圖(制定橫縱坐標(biāo))
xlabel('x(VSYNC Counter)','fontsize',12)
ylabel('y(TimeStamp)','fontsize',12)
set(gca,'linewidth',2)
%采用最小二乘擬合
Lxx=sum((x-mean(x)).^2)
Lxy=sum((x-mean(x)).*(y-mean(y)));
b1=Lxy/Lxx;
b0=mean(y)-b1*mean(x);
y1=b1*x+b0;
hold on
plot(x,y1,'linewidth',2);
m2=LinearModel.fit(x,y)%函數(shù)進(jìn)行線性回歸
最后我們擬合的直線如下圖所示,計算出來的斜率為8.29,賦給slope,表示SW vsync 周期

總結(jié)一下,Vsync模型先檢查驅(qū)動的周期以及驅(qū)動相鄰釋放的時間戳的差值是否都在誤差范圍內(nèi),如果都在誤差范圍內(nèi)則采樣6次有效的時間戳來進(jìn)行一元線性回歸,計算出直線的斜率和截距為后面預(yù)估Vsync喚醒時間做好準(zhǔn)備。
為啥需要預(yù)估,不一直使用驅(qū)動上報的時間戳?因為采樣6次后會disableHWVsync,剩下的都靠這條直線預(yù)估,如果一直打開HWVsync校準(zhǔn),會有功耗問題,相信做過顯示服務(wù)的同學(xué)曾經(jīng)都被功耗組提過單,說靜態(tài)桌面下測量電流比對比機高幾十ma,部分原因就是HW Vsync不斷在上報采樣。
掌握了這個模型,我們再來看系統(tǒng)如何根據(jù)現(xiàn)在的時間預(yù)估下一幀的時間。
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
//首先得到模型計算的slope和intercept值
auto const [slope, intercept] = getVSyncPredictionModelLocked();
// 當(dāng)打開HW Vsync時會先清掉之前的時間戳
if (mTimestamps.empty()) {
traceInt64If("VSP-mode", 1);
auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
return knownTimestamp + numPeriodsOut * mIdealPeriod;
}
/*
其核心思想是根據(jù)傳進(jìn)來的時間計算得到大于傳進(jìn)來的值且距離最近的slope倍數(shù)的值
可能有點拗口,舉個例子就知道了,我們簡化這些時間戳,比如:
x=[1,2,3,4,5,6];
y=[32,48,64,80,96,112];
當(dāng)我們傳入70時,根據(jù) y = ax+b, b=32, x=(70-32)/16 + 1 = 3 得到 y=3*16+32= 80
當(dāng)我們傳入100時,根據(jù) y = ax+b, b = 32,x=(100-32)/16 + 1 = 5 得到 y = 5*16 +32 = 112
也就是說我們輸出的值要是slope的倍數(shù),而且是大于傳進(jìn)來的值且距離最近的slope倍數(shù)的值
*/
auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
// See b/145667109, the ordinal calculation must take into account the intercept.
auto const zeroPoint = oldest + intercept;
auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
auto const prediction = (ordinalRequest * slope) + intercept + oldest;
traceInt64If("VSP-mode", 0);
traceInt64If("VSP-timePoint", timePoint);
traceInt64If("VSP-prediction", prediction);
auto const printer = [&, slope = slope, intercept = intercept] {
std::stringstream str;
str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
<< prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
<< "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
return str.str();
};
LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
printer().c_str());
return prediction;
}
到這里我們就知道了系統(tǒng)是如何預(yù)估時間戳了,這也保證了VSYNC-XXX的脈沖間隔都是周期的倍數(shù),后面可以把模型想象成黑盒子,只用管輸入和輸出就行。
回到VSyncDispatchTimerQueueEntry::schedule里面,這個mToken類型是sf。
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
/*根據(jù)前文所述,把nextAnticipatedVSyncTimeFrom想象成黑盒,輸入為now + timing.workDuration + timing.readyDuration
以60hz為例:
輸出得到nextVsyncTime值,mToken類型是sf,readyDuration = 0
nextVsyncTime的含義是這幀上屏?xí)r間(HW Vsync時間戳)
nextWakeupTime = nextVsyncTime - workDuration, 含義是預(yù)估的消費這一幀SW vsync喚醒的時間
理解這兩個變量很重要
也表明了,這一幀從sf合成到驅(qū)動上屏需要經(jīng)過1個workDuration時間。
*/
auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
return getExpectedCallbackTime(nextVsyncTime, timing);
}
bool const alreadyDispatchedForVsync = mLastDispatchTime &&
((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
(*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
if (alreadyDispatchedForVsync) {
nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
}
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
//這里更新mArmedInfo的mActualWakeupTime、mActualVsyncTime、mActualReadyTime
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
return getExpectedCallbackTime(nextVsyncTime, timing);
}
這里重在理解參與計算時間戳的含義,不然會一頭霧水,這里再強調(diào)一遍:
nextVsyncTime的含義是上屏的時間,也就是模擬出了HW Vsync時間,驅(qū)動根據(jù)HW Vsync來將幀上屏。
nextWakeupTime的含義是預(yù)估的消費這一幀SW vsync喚醒的時間,也就是模擬出了SW Vsync時間。
計算完后回到 VSyncDispatchTimerQueue::schedule里面:
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
...
/* 如果mActualWakeupTime < mIntendedWakeupTime - (0.5ms) 則走rearmTimerSkippingUpdateFor
更新喚醒時間
*/
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
return result;
}
mIntendedWakeupTime前面講過,表示VSYNC-XXX喚醒時間,當(dāng)這里計算出來的mActualWakeupTime 大于這個mIntendedWakeupTime時,需要以近的時間為主,就不會更新喚醒時間了。到這里scheduleFrame的邏輯就結(jié)束了。總結(jié)一下就是:根據(jù)當(dāng)前時間依靠Vsync模型計算出預(yù)估的HW Vsync和 SW Vsync喚醒時間,如果已經(jīng)有mIntendedWakeupTime 且喚醒時間大于mIntendedWakeupTime 時就結(jié)束,如果小于,則更新喚醒時間。
我們知道vsync是依據(jù)定時器實現(xiàn)在指定的時間喚醒,這個就是TimerDispatch 線程,根據(jù)epoll機制來監(jiān)聽TimerFd,當(dāng)定時時間到就會喚醒來執(zhí)行對應(yīng)的callback,這部分代碼在Timer.cpp里面,感興趣可以研究下,有了這部分基礎(chǔ),我們看下是哪里設(shè)的定時時間。
當(dāng)我們不清楚代碼是怎么跑,狀態(tài)量很多分不清楚時,就多加點log和trace來看,從現(xiàn)象反向理解原理。以60hz為例,發(fā)現(xiàn)走CallbackRepeater的callback時會通過schedule來重新設(shè)定喚醒時間。如下圖所示:

理解這部分原理需要聯(lián)系上下3幀來看,當(dāng)上一幀設(shè)定的定時時間到時,會喚醒callback,走如下邏輯:
void VSyncDispatchTimerQueue::timerCallback() {
struct Invocation {
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t vsyncTimestamp;
nsecs_t wakeupTimestamp;
nsecs_t deadlineTimestamp;
};
std::vector<Invocation> invocations;
{
std::lock_guard lock(mMutex);
auto const now = mTimeKeeper->now();
mLastTimerCallback = now;
// 這里mCallbacks size為3,分別為sf,app,appSf,所以會循環(huán)3次
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
auto const wakeupTime = callback->wakeupTime();
if (!wakeupTime) {
/* 當(dāng)mArmedInfo 為空時則不會把callback放到invocations里面,后面就不會
執(zhí)行對應(yīng)的callback函數(shù) */
continue;
}
auto const readyTime = callback->readyTime();
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
/*
當(dāng) mArmedInfo的mActualWakeupTime 小于 mIntendedWakeupTime + mTimerSlack + lagAllowance
則會把mArmedInfo reset掉,然后加入該類型的callback
*/
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
*wakeupTime, *readyTime});
}
}
// 將mIntendedWakeupTime 設(shè)置成一個很大的無效時間
mIntendedWakeupTime = kInvalidTime;
/* 再走一遍rearmTimerSkippingUpdateFor邏輯,如果前面都走了executing(),則cancelTimer
取消定時 */
rearmTimer(mTimeKeeper->now());
}
for (auto const& invocation : invocations) {
// 分別執(zhí)行對應(yīng)的callback函數(shù)
invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
invocation.deadlineTimestamp);
}
}
/* reset mArmedInfo*/
nsecs_t VSyncDispatchTimerQueueEntry::executing() {
mLastDispatchTime = mArmedInfo->mActualVsyncTime;
disarm();
return *mLastDispatchTime;
}
void VSyncDispatchTimerQueueEntry::disarm() {
mArmedInfo.reset();
}
/* 取消定時 */
void VSyncDispatchTimerQueue::cancelTimer() {
mIntendedWakeupTime = kInvalidTime;
mTimeKeeper->alarmCancel();
}
void Timer::alarmCancel() {
std::lock_guard lock(mMutex);
struct itimerspec old_timer;
struct itimerspec new_timer {
.it_interval = {.tv_sec = 0, .tv_nsec = 0},
.it_value = {
.tv_sec = 0,
.tv_nsec = 0,
},
};
if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
ALOGW("Failed to disarm timerfd");
}
}
sf對應(yīng)的callback為MessageQueue::vsyncCallback
void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
// Trace VSYNC-sf
// 觸發(fā)一次脈沖,VSYNC-SF 值發(fā)生變化
mVsync.value = (mVsync.value + 1) % 2;
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
mVsync.scheduledFrameTime.reset();
}
mTargetWakeupTime = targetWakeupTime;
const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime});
// 觸發(fā)SF合成工作開始
mHandler->dispatchFrame(vsyncId, vsyncTime);
}
app和appSf對應(yīng)的callback在CallbackRepeater,如下:
void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
{
std::lock_guard lock(mMutex);
mLastCallTime = std::chrono::nanoseconds(vsyncTime);
}
// mCallback 為 DispSyncSource::onVsyncCallback
mCallback(vsyncTime, wakeupTime, readyTime);
{
std::lock_guard lock(mMutex);
if (!mStarted) {
return;
}
//計算下一次的喚醒時間和HW Vsync時間
auto const scheduleResult =
mRegistration.schedule({.workDuration = mWorkDuration.count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = vsyncTime});
LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
}
...
}
void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
nsecs_t readyTime) {
VSyncSource::Callback* callback;
{
std::lock_guard lock(mCallbackMutex);
callback = mCallback;
}
// 觸發(fā)一次脈沖,VSYNC-app 或 VSYNC-appSf 值發(fā)生變化
if (mTraceVsync) {
mValue = (mValue + 1) % 2;
}
if (callback != nullptr) {
// 執(zhí)行EventThread::onVSyncEvent產(chǎn)生一個Event
callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime});
}
}
總結(jié)一下:當(dāng)上一幀設(shè)置定時,定時時間到,然后在回調(diào)函數(shù)觸發(fā)VSYNC-sf,VSYNC-app脈沖,sf開始合成工作,app開始繪制渲染工作。接著,app通過schedule重新計算下一幀的喚醒時間和HW Vsync時間,又回到了schedule,不同的是此時mToken為app和一些變量的不同,再看下這個Func:
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
ScheduleResult result;
{
std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
return result;
auto& callback = it->second;
auto const now = mTimeKeeper->now();
/* If the timer thread will run soon, we'll apply this work update via the callback
* timer recalculation to avoid cancelling a callback that is about to fire. */
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
callback->addPendingWorkloadUpdate(scheduleTiming);
return getExpectedCallbackTime(mTracker, now, scheduleTiming);
}
// 前面講過,這次要更新app的mArmedInfo
result = callback->schedule(scheduleTiming, mTracker, now);
if (!result.has_value()) {
return result;
}
/* 還記得當(dāng)token為sf時,這個條件不滿足,但此時因為執(zhí)行完回調(diào)的關(guān)系把mIntendedWakeupTime
設(shè)成一個非常大的無效數(shù),所以滿足了該條件, 需要重新設(shè)定喚醒時間
*/
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
return result;
}
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
// mCallbacks為3,循環(huán)3次,對應(yīng)app,sf,appsf
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
/* 如果對應(yīng)的mArmedInfo為空,則不會給定時器設(shè)置,這里sf和appsf都為空,因為
之前回調(diào)時清除了所有的mArmedInfo, 而app又通過 VSyncDispatchTimerQueueEntry::schedule
重新設(shè)置了mArmedInfo,所以只有app能設(shè)定時時間 */
if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
continue;
}
// 120hz會走update,因為sf和app vsync有相位差,這個后面再分析
if (it != skipUpdateIt) {
callback->update(mTracker, now);
}
// 獲取app VSyncDispatchTimerQueueEntry::schedule計算的喚醒時間
auto const wakeupTime = *callback->wakeupTime();
if (!min || *min > wakeupTime) {
nextWakeupName = callback->name();
min = wakeupTime;
targetVsync = callback->targetVsync();
}
}
if (min && min < mIntendedWakeupTime) {
if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
"us; VSYNC in ", ns2us(*targetVsync - now), "us");
ATRACE_NAME(trace.c_str());
}
// 將喚醒時間發(fā)送給定時器,終于找到了設(shè)置定時時間的地方
setTimer(*min, now);
} else {
cancelTimer();
}
}
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
// 更新mIntendedWakeupTime,無效變有效值
mIntendedWakeupTime = targetTime;
// 發(fā)送給定時器
mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
mIntendedWakeupTime);
mLastTimerSchedule = mTimeKeeper->now();
}
// 設(shè)置定時時間,記時開始,后面到mIntendedWakeupTime后再次喚醒
void Timer::alarmAt(std::function<void()> callback, nsecs_t time) {
std::lock_guard lock(mMutex);
using namespace std::literals;
static constexpr int ns_per_s =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
mCallback = std::move(callback);
mExpectingCallback = true;
struct itimerspec old_timer;
struct itimerspec new_timer {
.it_interval = {.tv_sec = 0, .tv_nsec = 0},
.it_value = {.tv_sec = static_cast<long>(time / ns_per_s),
.tv_nsec = static_cast<long>(time % ns_per_s)},
};
if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
總結(jié)一下:上一幀定時,當(dāng)前幀被喚醒后又開始在app schedule里面設(shè)置定時,到下一幀喚醒又開始設(shè)置定時,循環(huán)反復(fù)。還記得前面我們說的sf schedule沒有設(shè)置定時,到這里就清楚了,因為app schedule是在回調(diào)時設(shè)置的定時,而sf schedule 需要等應(yīng)用queueBuffer時才走,這個時間晚于app的schedule,計算出來的wakeupTime略大于mIntendedWakeupTime,所以按照app的schedule計算的喚醒時間來,如圖所示:

下面來講下120hz,有些流程與60hz不同,首先120hz與60hz相比,跟HW Vsync的相位差不同,sf vsync相比HW vsync提前2ms,app vsync相比HW vsync滯后1ms,這也決定了sf和app是分開定時的。
app phase: 1000000 ns SF phase: -2000000 ns
app duration: 13666666 ns SF duration: 10333333 ns
early app phase: 1000000 ns early SF phase: -2000000 ns
early app duration: 13666666 ns early SF duration: 10333333 ns
GL early app phase: 1000000 ns GL early SF phase: -2000000 ns
GL early app duration: 13666666 ns GL early SF duration: 10333333 ns
HWC min duration: 0 ns
present offset: 0 ns VSYNC period: 8333333 ns
當(dāng)觸發(fā)app回調(diào)時,依然從timerCallback進(jìn)行分析:
void VSyncDispatchTimerQueue::timerCallback() {
...
// 這里mCallbacks size為3,分別為sf,app,appSf,所以會循環(huán)3次
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
auto const wakeupTime = callback->wakeupTime();
if (!wakeupTime) {
continue;
}
auto const readyTime = callback->readyTime();
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
/*
120hz時sf的wakeupTime > mIntendedWakeupTime,所以不會執(zhí)行sf的callback
*/
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
*wakeupTime, *readyTime});
}
}
// 將mIntendedWakeupTime 設(shè)置成一個很大的無效時間
mIntendedWakeupTime = kInvalidTime;
/*
120hz sf的mArmedInfo 不會致空,會走到update里面
*/
rearmTimer(mTimeKeeper->now());
}
for (auto const& invocation : invocations) {
// 120hz 這里只執(zhí)行app的callback
invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
invocation.deadlineTimestamp);
}
}
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
// mCallbacks為3,循環(huán)3次,對應(yīng)app,sf,appsf
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
// 120hz sf沒有reset mArmedInfo,繼續(xù)往下執(zhí)行
if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
continue;
}
// 120hz sf會update,重新預(yù)估喚醒時間
if (it != skipUpdateIt) {
callback->update(mTracker, now);
}
auto const wakeupTime = *callback->wakeupTime();
if (!min || *min > wakeupTime) {
nextWakeupName = callback->name();
min = wakeupTime;
targetVsync = callback->targetVsync();
}
}
if (min && min < mIntendedWakeupTime) {
if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
"us; VSYNC in ", ns2us(*targetVsync - now), "us");
ATRACE_NAME(trace.c_str());
}
// 120hz 將sf喚醒時間發(fā)送給定時器
setTimer(*min, now);
} else {
cancelTimer();
}
}
/* update依然執(zhí)行nextAnticipatedVSyncTimeFrom來計算喚醒時間,此時計算的時間是vsync-sf下一次喚醒
時間 */
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
if (!mArmedInfo && !mWorkloadUpdateInfo) {
return;
}
if (mWorkloadUpdateInfo) {
mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync);
const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
}

當(dāng)sf回調(diào)時,狀態(tài)與app時相反,timerCallback時app的wakeupTime > mIntendedWakeupTime,所以不會執(zhí)行app的callback,app會走update計算下一次的VSYNC-app喚醒時間,然后設(shè)給定時器

總結(jié)一下:120hz sf和app是分開設(shè)置定時,app回調(diào)時sf的wakeupTime > mIntendedWakeupTime,故需要update sf的喚醒時間并設(shè)給定時器,sf回調(diào)時app的wakeupTime > mIntendedWakeupTime,故需要update app的喚醒時間并設(shè)給定時器,兩者交替進(jìn)行。如下圖所示是走app回調(diào),sf回調(diào)類似,就不上圖了。

分析到這里,我們就能回答開篇提的問題 "vsync是如何有序控制sf合成和app繪制的節(jié)奏?"
即根據(jù)vsync模型預(yù)估出大于傳入值且距離最近的周期的倍數(shù)來設(shè)置定時器喚醒sf和app的回調(diào),從而能讓sf和app以穩(wěn)定的周期間隔進(jìn)行合成和繪制。
相信大家都曾看到過這樣的trace,sf有一幀的周期變成了32ms,為什么這里沒有VSYNC-sf信號產(chǎn)生,可能有經(jīng)驗的同學(xué)都知道是因為應(yīng)用queueBuffer晚了或丟了一幀,確實如此,那代碼邏輯是怎樣的呢?

還是得從timerCallback說起,因為從上一幀定時到這一幀喚醒期間,sf都沒有更新mArmedInfo,還記得我們在上一幀執(zhí)行callback時會清空mArmedInfo,所以如果不更新mArmedInfo則不會將sf的callback放到invocations里面,自然也就不會觸發(fā)VSYNC-sf,而sf 更新mArmedInfo是通過scheduleFrame,所以應(yīng)用如果沒有queueBuffer則不會更新sf的mArmedInfo,所以也就不會觸發(fā)VSYNC-sf。
另外一種情況大家也都遇到過,即app有一幀周期變長了,為什么這里沒有VSYNC-app信號產(chǎn)生,有經(jīng)驗的同學(xué)也會想到因為應(yīng)用沒有requestVsync,那代碼邏輯是怎么樣的呢?

還是得從上一幀的timerCallback說起,當(dāng)app回調(diào)時會走onVsyncCallback,繼而會生成一個Vsync Event,并喚醒app的eventThread線程工作,代碼如下:
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
while (mState != State::Quit) {
std::optional<DisplayEventReceiver::Event> event;
// Determine next event to dispatch.
// 當(dāng)makeVsync時mPendingEvents不為空,取出該event
if (!mPendingEvents.empty()) {
event = mPendingEvents.front();
mPendingEvents.pop_front();
...
bool vsyncRequested = false;
// Find connections that should consume this event.
auto it = mDisplayEventConnections.begin();
while (it != mDisplayEventConnections.end()) {
if (const auto connection = it->promote()) {
// 是否有vsync請求
vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
// 如果有event和vsync請求,則會走后面dispatchEvent邏輯分發(fā)event給app
if (event && shouldConsumeEvent(*event, connection)) {
consumers.push_back(connection);
}
++it;
} else {
it = mDisplayEventConnections.erase(it);
}
}
if (!consumers.empty()) {
//分發(fā)Event給app,后續(xù)app就會做開篇說的doFrame等一套邏輯
dispatchEvent(*event, consumers);
consumers.clear();
}
State nextState;
/*
如果有vsync請求,則更新nextState,如果沒有,則設(shè)成idle
*/
if (mVSyncState && vsyncRequested) {
nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
} else {
ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
nextState = State::Idle;
}
/*
當(dāng)nextState 狀態(tài)變化時,會更新到mState
1. 當(dāng)之前的mState為State::VSync,nextState 為State::Idle,即走setVSyncEnabled(false)
這種情況簡單理解為前一次有vsync請求,這一次沒有
2. 當(dāng)之前的mState為State::Idle, nextState 為State::VSync,即走setVSyncEnabled(true)
這種情況簡單理解為前一次沒有vsync請求,這一次有了
*/
if (mState != nextState) {
if (mState == State::VSync) {
mVSyncSource->setVSyncEnabled(false);
} else if (nextState == State::VSync) {
mVSyncSource->setVSyncEnabled(true);
}
mState = nextState;
}
if (event) {
//如果有event則繼續(xù)下一次循環(huán)
continue;
}
// Wait for event or client registration/request.
// 如果mState 為idle則等待喚醒,線程處于sleep狀態(tài)
if (mState == State::Idle) {
mCondition.wait(lock);
} else {
...
}
}
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
//如果enable,即走app的schedule開始計算喚醒時間
if (enable) {
mCallbackRepeater->start(mWorkDuration, mReadyDuration);
} else {
//如果false,則走cancelTimer取消定時
mCallbackRepeater->stop();
}
mEnabled = enable;
}
void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
std::lock_guard lock(mMutex);
mStarted = true;
mWorkDuration = workDuration;
mReadyDuration = readyDuration;
//前一次沒有vsync請求,這一次有了,則開始計算喚醒時間
auto const scheduleResult =
mRegistration.schedule({.workDuration = mWorkDuration.count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = mLastCallTime.count()});
LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
}
void stop() {
std::lock_guard lock(mMutex);
LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
mStarted = false;
// 前一次有vsync請求,這一次沒有,則取消定時
mRegistration.cancel();
}
CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
return CancelResult::Error;
}
auto& callback = it->second;
auto const wakeupTime = callback->wakeupTime();
if (wakeupTime) {
//清除mArmedInfo
callback->disarm();
if (*wakeupTime == mIntendedWakeupTime) {
mIntendedWakeupTime = kInvalidTime;
//會走rearmTimerSkippingUpdateFor,前面已經(jīng)講過這個方法,走cancelTimer取消定時
rearmTimer(mTimeKeeper->now());
}
return CancelResult::Cancelled;
}
return CancelResult::TooLate;
}
總結(jié)一下,當(dāng)沒有vsync請求時,app通過一系列邏輯(見注釋)來取消定時,讓TimerDispatch 處于 sleep狀態(tài),trace也能佐證,所以后面的VSYNC-sf和VSYNC-app都沒產(chǎn)生

至此,我們對surfaceflinger內(nèi)部的vsync機制有了清晰的認(rèn)識。閱讀完本篇文章,希望幫助大家理解如下幾個原理:
- 通過HW Vsync采樣來對SW Vsync校準(zhǔn)的時機和流程
- SW Vsync計算模型
- 通過設(shè)定喚醒時間來觸發(fā)VSYNC-xxx脈沖讓app,sf進(jìn)行工作
- app,sf喚醒時間的計算