顯示框架之深入Vsync原理

本文旨在研究這個(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所示纤怒。


圖1

圖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è)原理:

  1. 通過HW Vsync采樣來對SW Vsync校準(zhǔn)的時(shí)機(jī)和流程
  2. SW Vsync計(jì)算模型
  3. 通過設(shè)定喚醒時(shí)間來觸發(fā)VSYNC-xxx脈沖讓app打掘,sf進(jìn)行工作
  4. app,sf喚醒時(shí)間的計(jì)算
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹏秋,一起剝皮案震驚了整個(gè)濱河市尊蚁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侣夷,老刑警劉巖横朋,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惜纸,居然都是意外死亡叶撒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門耐版,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祠够,“玉大人呛伴,你說我怎么就攤上這事泰演。” “怎么了亮靴?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵腺阳,是天一觀的道長落君。 經(jīng)常有香客問我,道長亭引,這世上最難降的妖魔是什么绎速? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮焙蚓,結(jié)果婚禮上纹冤,老公的妹妹穿的比我還像新娘。我一直安慰自己购公,他們只是感情好萌京,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宏浩,像睡著了一般知残。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上比庄,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天求妹,我揣著相機(jī)與錄音,去河邊找鬼印蔗。 笑死扒最,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的华嘹。 我是一名探鬼主播吧趣,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耙厚!你這毒婦竟也來了强挫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤薛躬,失蹤者是張志新(化名)和其女友劉穎俯渤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體型宝,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡八匠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年絮爷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梨树。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坑夯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抡四,到底是詐尸還是另有隱情柜蜈,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布指巡,位于F島的核電站淑履,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏藻雪。R本人自食惡果不足惜秘噪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阔涉。 院中可真熱鬧缆娃,春花似錦、人聲如沸瑰排。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椭住。三九已至崇渗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間京郑,已是汗流浹背宅广。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留些举,地道東北人跟狱。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像户魏,于是被迫代替她去往敵國和親驶臊。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容