vsync的介紹和由來網(wǎng)上介紹的有很多,個(gè)人理解vsync是統(tǒng)一app尤误、sf侠畔、lcm刷新的步調(diào),就好像人走路损晤,走的快和走的慢软棺。網(wǎng)上介紹都是從宏觀的角度分析vsync的原理,但作為底層工作者尤勋,還是需要從代碼層弄懂它實(shí)際工作的原理喘落。
vsync的基礎(chǔ)介紹:https://blog.csdn.net/zhaizu/article/details/51882768
vsync分為硬件vsync和軟件vsync,硬件vsync可以理解為屏幕的te信號(hào)最冰,當(dāng)hwc通過commit把數(shù)據(jù)提交給屏側(cè)時(shí)瘦棋,屏?xí)谙聜€(gè)te信號(hào)把數(shù)據(jù)刷出來;軟件vsync可以理解為在SurfaceFlinger內(nèi)部通過一套計(jì)算模型模擬硬件vsync暖哨。為什么需要在SurfaceFlinger里面搞一套計(jì)算模型赌朋?試想下,如果沒有篇裁,那SurfaceFlinger每一幀的刷新都需要接收從屏幕發(fā)過來的vsync沛慢,中間經(jīng)過了HWBinder調(diào)用,多增加調(diào)用就多一份功耗达布,當(dāng)然团甲,時(shí)間戳也不一定準(zhǔn)確,所以在SurfaceFlinger里面搞了一套軟件vsync計(jì)算模型黍聂。
模型的輸入為硬件vsync的時(shí)間戳:
文件:frameworks/native/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
// 先校驗(yàn)硬件te時(shí)間戳的有效性躺苦,如果無效,則繼續(xù)從屏幕那邊采集
if (!validate(timestamp)) {
// VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
// don't insert this ts into mTimestamps ringbuffer.
if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
} else {
mKnownTimestamp = timestamp;
}
return false;
}
// 如果硬件te信號(hào)有效产还,則把時(shí)間戳放在mTimestamps 隊(duì)列里面
if (mTimestamps.size() != kHistorySize) {
mTimestamps.push_back(timestamp);
mLastTimestampIndex = next(mLastTimestampIndex);
} else {
mLastTimestampIndex = next(mLastTimestampIndex);
mTimestamps[mLastTimestampIndex] = timestamp;
}
// 如果mTimestamps 的size小于6圾另,則繼續(xù)從屏幕那邊采集
if (mTimestamps.size() < kMinimumSamplesForPrediction) {
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)
// 這部分代碼是對(duì)輸入的6個(gè)時(shí)間戳做一個(gè)線性回歸,模擬出一條直線雕沉,這條直線的斜率就是vsync的周期集乔,截距是intercept
...
// 模型的輸出就是斜率和截距
it->second = {anticipatedPeriod, intercept};
ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
anticipatedPeriod, intercept);
return true;
}
文件:frameworks/native/services/surfaceflinger/Scheduler/Scheduler.cpp
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) {
needsHwVsync =
mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
}
}
if (needsHwVsync) {
enableHardwareVsync();
} else {
// 如果不再需要HW vsync,則采樣結(jié)束,關(guān)閉硬件Vsync
disableHardwareVsync(false);
}
}
void Scheduler::disableHardwareVsync(bool makeUnavailable) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
// 通知hwc,關(guān)掉硬件vsync
mEventControlThread->setVsyncEnabled(false);
mPrimaryDispSync->endResync();
// 將mPrimaryHWVsyncEnabled 設(shè)置為false, 這個(gè)是sf側(cè)是否打開hw vsync的標(biāo)志
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
mHWVsyncAvailable = false;
}
}
其實(shí)軟件Vsync的計(jì)算模型就是簡(jiǎn)單的線性回歸涯保,采樣6個(gè)硬件 te信號(hào)彬向,來擬合出Surfaceflinger要跑的vsync周期和截距,這個(gè)截距的作用還不是很清楚宫莱。采樣完畢后,將硬件Vsync關(guān)閉哩罪。
接下來看下授霸,SurfaceFlinger是如何利用模型的輸出值計(jì)算下一個(gè)vsync的時(shí)間戳。
從trace來看际插,TimerDispatch線程會(huì)執(zhí)行vsync的callback來創(chuàng)建vsync event碘耳,app拿這個(gè)vsync event喚醒a(bǔ)pp的EventThread去給應(yīng)用繪制,sf拿這個(gè)vsync event喚醒sf的EventThread去刷幀框弛,之后再計(jì)算下一個(gè)vsync的時(shí)間戳辛辨,重復(fù)如此。代碼從這里切入進(jìn)來看:
文件:frameworks/native/services/surfaceflinger/Scheduler/VSyncReactor.cpp
void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
{
std::lock_guard<std::mutex> lk(mMutex);
mLastCallTime = vsynctime;
}
// mCallback 是DispSyncSource對(duì)象
mCallback->onDispSyncEvent(wakeupTime, vsynctime);
{
std::lock_guard<std::mutex> lk(mMutex);
if (mStopped) {
return;
}
// 計(jì)算下一個(gè)vsync時(shí)間戳
auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
"Error rescheduling callback: rc %X", schedule_result);
}
}
文件:frameworks/native/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
nsecs_t earliestVsync) {
...
// 這里的callback為VSyncDispatchTimerQueueEntry 對(duì)象
result = callback->schedule(workDuration, earliestVsync, mTracker, now);
if (result == ScheduleResult::CannotSchedule) {
return result;
}
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
return result;
}
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
VSyncTracker& tracker, nsecs_t now) {
// 計(jì)算下一個(gè)vsync的時(shí)間戳
auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
...
// 下一個(gè)喚醒時(shí)間是下一個(gè)vsync時(shí)間-workDuration, workDuration = Vsync period - offset
auto const nextWakeupTime = nextVsyncTime - workDuration;
mWorkDuration = workDuration;
mEarliestVsync = earliestVsync;
// 更新mActualWakeupTime 和 mActualVsyncTime 值
mArmedInfo = {nextWakeupTime, nextVsyncTime};
return ScheduleResult::Scheduled;
}
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;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
...
auto const wakeupTime = *callback->wakeupTime();
if (!min || (min && *min > wakeupTime)) {
// 將喚醒時(shí)間設(shè)給min
nextWakeupName = callback->name();
min = wakeupTime;
targetVsync = callback->targetVsync();
}
}
if (min && (min < mIntendedWakeupTime)) {
if (targetVsync && nextWakeupName) {
mTraceBuffer.note(*nextWakeupName, *min - now, *targetVsync - now);
}
// 給定時(shí)器輸入時(shí)間
setTimer(*min, now);
} else {
ATRACE_NAME("cancel timer");
cancelTimer();
}
}
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
mIntendedWakeupTime = targetTime;
// 定時(shí)器定時(shí)的時(shí)間是 mActualWakeupTime - now
mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
targetTime - now);
mLastTimerSchedule = mTimeKeeper->now();
}
文件:frameworks/native/services/surfaceflinger/Scheduler/Timer.cpp
void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
...
// 把callback帶進(jìn)來
mCallback = cb;
// 時(shí)間單位轉(zhuǎn)換
struct itimerspec old_timer;
struct itimerspec new_timer {
.it_interval = {.tv_sec = 0, .tv_nsec = 0},
.it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
.tv_nsec = static_cast<long>(fireIn % ns_per_s)},
};
// 在Timer::reset 時(shí)創(chuàng)建了一個(gè) mTimerFd瑟枫,可以理解為創(chuàng)建了一個(gè)定時(shí)器斗搞,然后設(shè)置了定時(shí)的時(shí)間
if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
利用vsync模型值的函數(shù)在nextAnticipatedVSyncTimeFrom 邏輯里面,這個(gè)函數(shù)也是計(jì)算下一個(gè)vsync時(shí)間戳的最主要的函數(shù)慷妙,來看下
文件: frameworks/native/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard<std::mutex> lk(mMutex);
//從vsync 計(jì)算模型獲得值僻焚,slope表示計(jì)算出來的vsync period,intercept表示截距
auto const [slope, intercept] = getVSyncPredictionModel(lk);
...
// 從mTimestamps 時(shí)間戳拿最小的一個(gè)時(shí)間
auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
// See b/145667109, the ordinal calculation must take into account the intercept.
// 這套算式大致理解為 prediction 約等于 timepoint + slope膝擂, 為什么是約等于溅呢,因?yàn)槿∮噙\(yùn)算是個(gè)陷阱
auto const zeroPoint = oldest + intercept;
auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
auto const prediction = (ordinalRequest * slope) + intercept + oldest;
...
return prediction;
}
對(duì)于這個(gè)運(yùn)算,其實(shí)可以這么簡(jiǎn)單理解猿挚,now + workDuration + slope 約等于 nextVsyncTime , 而 wakeuptime = nextVsyncTime - workDuration咐旧,所以給定時(shí)器設(shè)置的時(shí)間就是 slope,也就是過一個(gè)vsync 周期绩蜻,回調(diào)一次铣墨。