本文旨在研究這個(gè)話題-- vsync是如何有序控制sf合成和app繪制的節(jié)奏?
應(yīng)用需要等VSYNC-app脈沖來進(jìn)行繪制,繪制完后又需要等VSYNC-sf脈沖在surfaceflinger里面進(jìn)行合成假夺,VSYNC-app和VSYNC-sf的觸發(fā)是這篇文章的重點(diǎn)靶累,先來回顧下繪制->合成的整個(gè)鏈路,大致如下:{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)用要更新幀徙歼,先看下這個(gè)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這兩個(gè)值犁河,這兩個(gè)值參與了vsync的計(jì)算。該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類的一個(gè)全局變量狭园,通過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則不會(huì)執(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 這個(gè)func构订,schedule是計(jì)算vsync時(shí)間戳的入口,如下:
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;
}
這里有個(gè)mToken來表示具體哪種類型的回調(diào)挤渐,在初始化時(shí)注冊了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里面徙鱼,此時(shí)mToken表示sf的callback宅楞,mIntendedWakeupTime表示這幀預(yù)期喚醒時(shí)間,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);
}
這個(gè)Func的一堆時(shí)間戳看的頭暈眼花,可以嘗試把這些時(shí)間戳打在trace上一幀幀查看绞绒。真正計(jì)算vsync時(shí)間戳的Func是nextAnticipatedVSyncTimeFrom婶希,在講這個(gè)函數(shù)前需要介紹下vsync計(jì)算模型,了解了這個(gè)模型蓬衡,后面再看到nextAnticipatedVSyncTimeFrom就可以只用看輸入的時(shí)間戳以及對應(yīng)的輸出喻杈。
Vsync計(jì)算模型
從切幀的角度來看這個(gè)模型彤枢,當(dāng)系統(tǒng)發(fā)生切幀時(shí),會(huì)通過resyncToHardwareVsync->setVsyncPeriod->
setVsyncEnabled 來打開HW Vsync校準(zhǔn)SW Vsync到預(yù)期的周期筒饰,如下
void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
...
if (display->setDesiredActiveMode(info)) {
// 讓sf強(qiáng)行合成一幀
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)閉或者強(qiáng)行打開則使能瓷们,
告訴display驅(qū)動(dòng)需要校準(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);
}
}
//不一致則會(huì)把把period設(shè)給mPeriodTransitioningTo谬晕,然后需要采樣
void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
ATRACE_CALL();
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
setIgnorePresentFencesInternal(true);
}
切幀時(shí)會(huì)設(shè)一次scheduleComposite讓sf強(qiáng)行合成一次碘裕,這一次合成的目的是在setActiveModeInHwcIfNeeded 時(shí)把預(yù)期的mode傳給HWC,這樣讓display驅(qū)動(dòng)切到預(yù)期的mode后就能往上報(bào)時(shí)間戳來校準(zhǔn)SW Vsync。試想如果不強(qiáng)行合成一幀,如果下一幀沒有應(yīng)用transaction的變化,sf不合成,就無法將正確的mode傳遞給驅(qū)動(dòng)闷串。
void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
...
// 將預(yù)期的mode傳遞給驅(qū)動(dòng)
const auto status = FTL_FAKE_GUARD(kMainThreadContext,
display->initiateModeChange(*desiredActiveMode,
constraints, &outTimeline));
...
}
驅(qū)動(dòng)切幀時(shí)會(huì)上報(bào)時(shí)間戳給到surfaceflinger進(jìn)行校準(zhǔn),如下:
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
...
//驅(qū)動(dòng)會(huì)傳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ū)動(dòng)的周期和設(shè)的mPeriodTransitioningTo是否在誤差范圍內(nèi),如果在誤差范圍內(nèi)則表示
驅(qū)動(dòng)已經(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ū)動(dòng)傳來的時(shí)間戳加進(jìn)來校準(zhǔn)
mTracker.addVsyncTimestamp(timestamp);
/* 將 mPeriodConfirmationInProgress設(shè)為false揩页,表示已經(jīng)確認(rèn)驅(qū)動(dòng)的周期是正確的,接下來
不會(huì)再走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 時(shí)會(huì)走”adding sample“ 繼續(xù)添加時(shí)間戳
mTracker.addVsyncTimestamp(timestamp);
// 如果收集到的mTimestamps size小于6爆侣,則繼續(xù)進(jìn)行采樣
mMoreSamplesNeeded = mTracker.needsMoreSamples();
}
if (!mMoreSamplesNeeded) {
setIgnorePresentFencesInternal(false);
}
return mMoreSamplesNeeded;
}
可以看到addHwVsyncTimestamp 是先判斷驅(qū)動(dòng)的周期是否成功切換為預(yù)期值,如果成功幢妄,則走"adding sample" 來添加時(shí)間戳兔仰,如果失敗,則需要繼續(xù)走periodConfirmed判斷驅(qū)動(dòng)的周期蕉鸳。
Vsync模型的核心在addVsyncTimestamp體現(xiàn)乎赴,Google也是很友好的寫了注釋來解釋這個(gè)模型,其實(shí)就是簡單的一元線性回歸潮尝,用來確定2個(gè)變量之間存在定量關(guān)系的統(tǒng)計(jì)方法榕吼,將兩個(gè)變量用一條直線近似表示。
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { std::lock_guard lock(mMutex);
/* validate 來判斷驅(qū)動(dòng)前后兩幀的時(shí)間戳差值與mIdealPeriod 的誤差勉失。
正常情況下驅(qū)動(dòng)上傳的前后兩幀時(shí)間戳的差應(yīng)該與 mIdealPeriod相差不大羹蚣,如果誤差較大,則可能驅(qū)動(dòng)
產(chǎn)生的時(shí)間戳有問題 */
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;
}
// 如果時(shí)間戳有效乱凿,則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) {
//如果有效的時(shí)間戳小于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個(gè)時(shí),使用一元線性回歸胁出,將采集到的mTimestamps時(shí)間戳當(dāng)成Y型型,序號(hào)當(dāng)作X
計(jì)算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ū)動(dòng)前后兩幀的時(shí)間戳差值與mIdealPeriod 的誤差划鸽,若為有效的時(shí)間戳則加入到mTimestamps里面输莺,收集滿6個(gè)則采用一元線性回歸擬合時(shí)間戳與序列號(hào)成直線關(guān)系,為后面預(yù)估vsync時(shí)間戳做準(zhǔn)備裸诽。按照Google的注釋嫂用,我們用Matlab畫個(gè)圖,我們先收集6個(gè)有效的時(shí)間戳丈冬,如下圖:
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*') %作散點(diǎn)圖(制定橫縱坐標(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)行線性回歸
最后我們擬合的直線如下圖所示嘱函,計(jì)算出來的斜率為8.29,賦給slope埂蕊,表示SW vsync 周期
總結(jié)一下往弓,Vsync模型先檢查驅(qū)動(dòng)的周期以及驅(qū)動(dòng)相鄰釋放的時(shí)間戳的差值是否都在誤差范圍內(nèi),如果都在誤差范圍內(nèi)則采樣6次有效的時(shí)間戳來進(jìn)行一元線性回歸蓄氧,計(jì)算出直線的斜率和截距為后面預(yù)估Vsync喚醒時(shí)間做好準(zhǔn)備函似。
為啥需要預(yù)估,不一直使用驅(qū)動(dòng)上報(bào)的時(shí)間戳喉童?因?yàn)椴蓸?次后會(huì)disableHWVsync撇寞,剩下的都靠這條直線預(yù)估,如果一直打開HWVsync校準(zhǔn)堂氯,會(huì)有功耗問題蔑担,相信做過顯示服務(wù)的同學(xué)曾經(jīng)都被功耗組提過單,說靜態(tài)桌面下測量電流比對比機(jī)高幾十ma咽白,部分原因就是HW Vsync不斷在上報(bào)采樣啤握。
掌握了這個(gè)模型,我們再來看系統(tǒng)如何根據(jù)現(xiàn)在的時(shí)間預(yù)估下一幀的時(shí)間晶框。
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
//首先得到模型計(jì)算的slope和intercept值
auto const [slope, intercept] = getVSyncPredictionModelLocked();
// 當(dāng)打開HW Vsync時(shí)會(huì)先清掉之前的時(shí)間戳
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)來的時(shí)間計(jì)算得到大于傳進(jìn)來的值且距離最近的slope倍數(shù)的值
可能有點(diǎn)拗口排抬,舉個(gè)例子就知道了,我們簡化這些時(shí)間戳三妈,比如:
x=[1,2,3,4,5,6];
y=[32畜埋,48,64畴蒲,80悠鞍,96,112];
當(dāng)我們傳入70時(shí),根據(jù) y = ax+b咖祭, b=32, x=(70-32)/16 + 1 = 3 得到 y=3*16+32= 80
當(dāng)我們傳入100時(shí)掩宜,根據(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ù)估時(shí)間戳了牺汤,這也保證了VSYNC-XXX的脈沖間隔都是周期的倍數(shù),后面可以把模型想象成黑盒子浩嫌,只用管輸入和輸出就行檐迟。
回到VSyncDispatchTimerQueueEntry::schedule里面,這個(gè)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時(shí)間戳)
nextWakeupTime = nextVsyncTime - workDuration, 含義是預(yù)估的消費(fèi)這一幀SW vsync喚醒的時(shí)間
理解這兩個(gè)變量很重要
也表明了束铭,這一幀從sf合成到驅(qū)動(dòng)上屏需要經(jīng)過1個(gè)workDuration時(shí)間廓块。
*/
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);
}
這里重在理解參與計(jì)算時(shí)間戳的含義带猴,不然會(huì)一頭霧水,這里再強(qiáng)調(diào)一遍:
nextVsyncTime的含義是上屏的時(shí)間懈万,也就是模擬出了HW Vsync時(shí)間浓利,驅(qū)動(dòng)根據(jù)HW Vsync來將幀上屏。
nextWakeupTime的含義是預(yù)估的消費(fèi)這一幀SW vsync喚醒的時(shí)間钞速,也就是模擬出了SW Vsync時(shí)間。
計(jì)算完后回到 VSyncDispatchTimerQueue::schedule里面:
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
ScheduleTiming scheduleTiming) {
...
/* 如果mActualWakeupTime < mIntendedWakeupTime - (0.5ms) 則走rearmTimerSkippingUpdateFor
更新喚醒時(shí)間
*/
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
return result;
}
mIntendedWakeupTime前面講過嫡秕,表示VSYNC-XXX喚醒時(shí)間渴语,當(dāng)這里計(jì)算出來的mActualWakeupTime 大于這個(gè)mIntendedWakeupTime時(shí),需要以近的時(shí)間為主昆咽,就不會(huì)更新喚醒時(shí)間了驾凶。到這里scheduleFrame的邏輯就結(jié)束了≈佬铮總結(jié)一下就是:根據(jù)當(dāng)前時(shí)間依靠Vsync模型計(jì)算出預(yù)估的HW Vsync和 SW Vsync喚醒時(shí)間调违,如果已經(jīng)有mIntendedWakeupTime 且喚醒時(shí)間大于mIntendedWakeupTime 時(shí)就結(jié)束,如果小于泻轰,則更新喚醒時(shí)間技肩。
我們知道vsync是依據(jù)定時(shí)器實(shí)現(xiàn)在指定的時(shí)間喚醒,這個(gè)就是TimerDispatch 線程浮声,根據(jù)epoll機(jī)制來監(jiān)聽TimerFd虚婿,當(dāng)定時(shí)時(shí)間到就會(huì)喚醒來執(zhí)行對應(yīng)的callback旋奢,這部分代碼在Timer.cpp里面,感興趣可以研究下然痊,有了這部分基礎(chǔ)至朗,我們看下是哪里設(shè)的定時(shí)時(shí)間。
當(dāng)我們不清楚代碼是怎么跑剧浸,狀態(tài)量很多分不清楚時(shí)锹引,就多加點(diǎn)log和trace來看,從現(xiàn)象反向理解原理唆香。以60hz為例嫌变,發(fā)現(xiàn)走CallbackRepeater的callback時(shí)會(huì)通過schedule來重新設(shè)定喚醒時(shí)間。如下圖所示:
理解這部分原理需要聯(lián)系上下3幀來看袋马,當(dāng)上一幀設(shè)定的定時(shí)時(shí)間到時(shí)初澎,會(huì)喚醒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ì)循環(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 為空時(shí)則不會(huì)把callback放到invocations里面,后面就不會(huì)
執(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
則會(huì)把mArmedInfo reset掉桑谍,然后加入該類型的callback
*/
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
*wakeupTime, *readyTime});
}
}
// 將mIntendedWakeupTime 設(shè)置成一個(gè)很大的無效時(shí)間
mIntendedWakeupTime = kInvalidTime;
/* 再走一遍rearmTimerSkippingUpdateFor邏輯延柠,如果前面都走了executing(),則cancelTimer
取消定時(shí) */
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();
}
/* 取消定時(shí) */
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;
}
//計(jì)算下一次的喚醒時(shí)間和HW Vsync時(shí)間
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)生一個(gè)Event
callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime});
}
}
總結(jié)一下:當(dāng)上一幀設(shè)置定時(shí)雹仿,定時(shí)時(shí)間到增热,然后在回調(diào)函數(shù)觸發(fā)VSYNC-sf,VSYNC-app脈沖胧辽,sf開始合成工作峻仇,app開始繪制渲染工作。接著邑商,app通過schedule重新計(jì)算下一幀的喚醒時(shí)間和HW Vsync時(shí)間摄咆,又回到了schedule,不同的是此時(shí)mToken為app和一些變量的不同人断,再看下這個(gè)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時(shí),這個(gè)條件不滿足恶迈,但此時(shí)因?yàn)閳?zhí)行完回調(diào)的關(guān)系把mIntendedWakeupTime
設(shè)成一個(gè)非常大的無效數(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為空,則不會(huì)給定時(shí)器設(shè)置鸭廷,這里sf和appsf都為空枣抱,因?yàn)? 之前回調(diào)時(shí)清除了所有的mArmedInfo, 而app又通過 VSyncDispatchTimerQueueEntry::schedule
重新設(shè)置了mArmedInfo辆床,所以只有app能設(shè)定時(shí)時(shí)間 */
if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
continue;
}
// 120hz會(huì)走update佳晶,因?yàn)閟f和app vsync有相位差,這個(gè)后面再分析
if (it != skipUpdateIt) {
callback->update(mTracker, now);
}
// 獲取app VSyncDispatchTimerQueueEntry::schedule計(jì)算的喚醒時(shí)間
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());
}
// 將喚醒時(shí)間發(fā)送給定時(shí)器讼载,終于找到了設(shè)置定時(shí)時(shí)間的地方
setTimer(*min, now);
} else {
cancelTimer();
}
}
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
// 更新mIntendedWakeupTime轿秧,無效變有效值
mIntendedWakeupTime = targetTime;
// 發(fā)送給定時(shí)器
mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
mIntendedWakeupTime);
mLastTimerSchedule = mTimeKeeper->now();
}
// 設(shè)置定時(shí)時(shí)間,記時(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é)一下:上一幀定時(shí)菇篡,當(dāng)前幀被喚醒后又開始在app schedule里面設(shè)置定時(shí),到下一幀喚醒又開始設(shè)置定時(shí)一喘,循環(huán)反復(fù)驱还。還記得前面我們說的sf schedule沒有設(shè)置定時(shí),到這里就清楚了凸克,因?yàn)閍pp schedule是在回調(diào)時(shí)設(shè)置的定時(shí)议蟆,而sf schedule 需要等應(yīng)用queueBuffer時(shí)才走,這個(gè)時(shí)間晚于app的schedule萎战,計(jì)算出來的wakeupTime略大于mIntendedWakeupTime咐容,所以按照app的schedule計(jì)算的喚醒時(shí)間來,如圖所示:
下面來講下120hz蚂维,有些流程與60hz不同戳粒,首先120hz與60hz相比,跟HW Vsync的相位差不同虫啥,sf vsync相比HW vsync提前2ms蔚约,app vsync相比HW vsync滯后1ms,這也決定了sf和app是分開定時(shí)的涂籽。
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)時(shí)炊琉,依然從timerCallback進(jìn)行分析:
void VSyncDispatchTimerQueue::timerCallback() {
...
// 這里mCallbacks size為3,分別為sf,app,appSf又活,所以會(huì)循環(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時(shí)sf的wakeupTime > mIntendedWakeupTime,所以不會(huì)執(zhí)行sf的callback
*/
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
*wakeupTime, *readyTime});
}
}
// 將mIntendedWakeupTime 設(shè)置成一個(gè)很大的無效時(shí)間
mIntendedWakeupTime = kInvalidTime;
/*
120hz sf的mArmedInfo 不會(huì)致空锰悼,會(huì)走到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會(huì)update箕般,重新預(yù)估喚醒時(shí)間
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喚醒時(shí)間發(fā)送給定時(shí)器
setTimer(*min, now);
} else {
cancelTimer();
}
}
/* update依然執(zhí)行nextAnticipatedVSyncTimeFrom來計(jì)算喚醒時(shí)間耐薯,此時(shí)計(jì)算的時(shí)間是vsync-sf下一次喚醒
時(shí)間 */
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)時(shí),狀態(tài)與app時(shí)相反,timerCallback時(shí)app的wakeupTime > mIntendedWakeupTime曲初,所以不會(huì)執(zhí)行app的callback体谒,app會(huì)走update計(jì)算下一次的VSYNC-app喚醒時(shí)間,然后設(shè)給定時(shí)器
總結(jié)一下:120hz sf和app是分開設(shè)置定時(shí)臼婆,app回調(diào)時(shí)sf的wakeupTime > mIntendedWakeupTime抒痒,故需要update sf的喚醒時(shí)間并設(shè)給定時(shí)器,sf回調(diào)時(shí)app的wakeupTime > mIntendedWakeupTime颁褂,故需要update app的喚醒時(shí)間并設(shè)給定時(shí)器故响,兩者交替進(jìn)行。如下圖所示是走app回調(diào)颁独,sf回調(diào)類似彩届,就不上圖了。
分析到這里誓酒,我們就能回答開篇提的問題 "vsync是如何有序控制sf合成和app繪制的節(jié)奏樟蠕?"
即根據(jù)vsync模型預(yù)估出大于傳入值且距離最近的周期的倍數(shù)來設(shè)置定時(shí)器喚醒sf和app的回調(diào),從而能讓sf和app以穩(wěn)定的周期間隔進(jìn)行合成和繪制靠柑。
相信大家都曾看到過這樣的trace寨辩,sf有一幀的周期變成了32ms,為什么這里沒有VSYNC-sf信號(hào)產(chǎn)生病往,可能有經(jīng)驗(yàn)的同學(xué)都知道是因?yàn)閼?yīng)用queueBuffer晚了或丟了一幀捣染,確實(shí)如此,那代碼邏輯是怎樣的呢停巷?
還是得從timerCallback說起耍攘,因?yàn)閺纳弦粠〞r(shí)到這一幀喚醒期間,sf都沒有更新mArmedInfo畔勤,還記得我們在上一幀執(zhí)行callback時(shí)會(huì)清空mArmedInfo蕾各,所以如果不更新mArmedInfo則不會(huì)將sf的callback放到invocations里面,自然也就不會(huì)觸發(fā)VSYNC-sf庆揪,而sf 更新mArmedInfo是通過scheduleFrame式曲,所以應(yīng)用如果沒有queueBuffer則不會(huì)更新sf的mArmedInfo,所以也就不會(huì)觸發(fā)VSYNC-sf缸榛。
另外一種情況大家也都遇到過吝羞,即app有一幀周期變長了,為什么這里沒有VSYNC-app信號(hào)產(chǎn)生内颗,有經(jīng)驗(yàn)的同學(xué)也會(huì)想到因?yàn)閼?yīng)用沒有requestVsync钧排,那代碼邏輯是怎么樣的呢?
還是得從上一幀的timerCallback說起均澳,當(dāng)app回調(diào)時(shí)會(huì)走onVsyncCallback恨溜,繼而會(huì)生成一個(gè)Vsync Event符衔,并喚醒a(bǔ)pp的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時(shí)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請求,則會(huì)走后面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就會(huì)做開篇說的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)變化時(shí),會(huì)更新到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開始計(jì)算喚醒時(shí)間
if (enable) {
mCallbackRepeater->start(mWorkDuration, mReadyDuration);
} else {
//如果false水慨,則走cancelTimer取消定時(shí)
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請求,這一次有了敬扛,則開始計(jì)算喚醒時(shí)間
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請求晰洒,這一次沒有,則取消定時(shí)
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;
//會(huì)走rearmTimerSkippingUpdateFor啥箭,前面已經(jīng)講過這個(gè)方法谍珊,走cancelTimer取消定時(shí)
rearmTimer(mTimeKeeper->now());
}
return CancelResult::Cancelled;
}
return CancelResult::TooLate;
}
總結(jié)一下,當(dāng)沒有vsync請求時(shí)急侥,app通過一系列邏輯(見注釋)來取消定時(shí)砌滞,讓TimerDispatch 處于 sleep狀態(tài),trace也能佐證坏怪,所以后面的VSYNC-sf和VSYNC-app都沒產(chǎn)生
至此贝润,我們對surfaceflinger內(nèi)部的vsync機(jī)制有了清晰的認(rèn)識(shí)。閱讀完本篇文章铝宵,希望幫助大家理解如下幾個(gè)原理:
- 通過HW Vsync采樣來對SW Vsync校準(zhǔn)的時(shí)機(jī)和流程
- SW Vsync計(jì)算模型
- 通過設(shè)定喚醒時(shí)間來觸發(fā)VSYNC-xxx脈沖讓app打掘,sf進(jìn)行工作
- app,sf喚醒時(shí)間的計(jì)算