[086]VSYNC研究-最后的窗戶紙

背景

最近在精讀努比亞團(tuán)隊(duì)的SurfaceFlinger模塊-VSYNC研究,其中有一段話一直困擾到我,成為了徹底理解vsync的最后一層窗戶紙茴肥。

3.2.2 nextAnticipatedVsyncTimeFromLocked
有了這個(gè)回歸系數(shù)和截距瓤狐,就可以傳入上一次app或者sf發(fā)射的時(shí)間础锐,計(jì)算出下一次發(fā)射的時(shí)間

一皆警、我的疑問(wèn)

按照他的說(shuō)法,每次nextVsync都是依賴上一次app或者sf發(fā)射的時(shí)間earliestVsync鸵隧,那問(wèn)題來(lái)了豆瘫,如果屏幕界面停止刷新一段時(shí)間外驱,然后app再次刷新昵宇,那這時(shí)候上一次app發(fā)射的時(shí)間earliestVsync遠(yuǎn)遠(yuǎn)早于希望拿到的nextVsync一個(gè)Vsync周期以上磅崭,按照計(jì)算公式,根本算不出正確nextVsync趟薄。

舉個(gè)例子
假設(shè)周期16ms绽诚,上次vsync的時(shí)間為16ms,然后過(guò)了160ms杭煎,我這個(gè)時(shí)候請(qǐng)求nextVsync恩够。
因?yàn)閑arliestVsync是16ms,算出來(lái)的應(yīng)該是32ms羡铲,但是實(shí)際的nextVsync應(yīng)該是16 + 160 + 16 = 192 ms

二蜂桶、傳入的實(shí)際數(shù)據(jù)

其實(shí)傳入的實(shí)際數(shù)據(jù)是timing.earliestVsyncnow + timing.workDuration + timing.readyDuration兩個(gè)數(shù)字的中最大值也切,而且絕大多數(shù)還是用后者這個(gè)數(shù)字

    auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));

workDurationreadyDuration與我之前文章寫(xiě)的有關(guān)[070]一文帶你看懂Vsync Phase扑媚,建議先看這個(gè)文章。

舉個(gè)例子

我通過(guò)dumpsys SurfaceFlinger | grep duration雷恃,獲取我的設(shè)備參數(shù)

XXXXX:/ $ dumpsys SurfaceFlinger | grep duration
           app duration:  16666666 ns            SF duration:  15666666 ns

最后作用到app和sf的vsync如下

vsync類型 workDuration readyDuration
app vsync app duration(16666666 ns) SF duration (15666666 ns)
sf vsync SF duration(15666666 ns) 0

很明顯now + timing.workDuration + timing.readyDuration疆股,用這個(gè)值作為下面函數(shù)timePoint就可以計(jì)算出nextVsync。

不知道怎么計(jì)算的可以參考我前面的文章[085]SW VSYNC模型更新與校準(zhǔn)

nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
    auto const [slope, intercept] = getVSyncPredictionModelLocked();

    if (mTimestamps.empty()) {
        traceInt64If("VSP-mode", 1);
        auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
        auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
        return knownTimestamp + numPeriodsOut * mIdealPeriod;
    }

    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();
    };

    ALOGV("%s", printer().c_str());
    LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
                        printer().c_str());

    return prediction;
}

三倒槐、繼續(xù)思考

到這里其實(shí)還有幾點(diǎn)疑惑旬痹,需要進(jìn)一步解答

3.1 等于每次app要申請(qǐng)的時(shí)候,會(huì)走到resyncAndRefresh中讨越,這個(gè)函數(shù)就會(huì)強(qiáng)制進(jìn)行一次硬件的VSYNC校準(zhǔn)两残。

這句話明顯不可能,因?yàn)閠race中可以看到hw vsync正常刷新的時(shí)候就會(huì)關(guān)閉把跨。
合理的說(shuō)法應(yīng)該是如果兩次app vsync的request nextvsync時(shí)間差大于750ms人弓,就會(huì)觸發(fā)一下hardware vsync同步。

/frameworks/native/services/surfaceflinger/Scheduler/Scheduler.cpp

void Scheduler::resync() {
    static constexpr nsecs_t kIgnoreDelay = ms2ns(750);//如果兩次app vsync的request 

    const nsecs_t now = systemTime();
    const nsecs_t last = mLastResyncTime.exchange(now);

    if (now - last > kIgnoreDelay) {
        resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod()); //硬件校準(zhǔn)
    }
}

我通過(guò)住trace着逐,驗(yàn)證了這個(gè)


3.2 假如觸發(fā)了硬件的VSYNC校準(zhǔn)崔赌,就會(huì)清空模型中的數(shù)據(jù)意蛀,如果算出nextVsync

關(guān)鍵看這個(gè)代碼,一旦mTimestamps為空峰鄙,就會(huì)用mKnownTimestamp來(lái)計(jì)算浸间,也就是"VSP-mode"為1.

nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
    auto const [slope, intercept] = getVSyncPredictionModelLocked();

    if (mTimestamps.empty()) {
        traceInt64If("VSP-mode", 1);
        auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
        auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
        return knownTimestamp + numPeriodsOut * mIdealPeriod;
    }

mKnownTimestamp 又是在clear的時(shí)候被賦值了采樣的vsync中的最大值

void VSyncPredictor::clearTimestamps() {
    if (!mTimestamps.empty()) {
        auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
        if (mKnownTimestamp) {
            mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
        } else {
            mKnownTimestamp = maxRb;
        }

        mTimestamps.clear();
        mLastTimestampIndex = 0;
    }
}
簡(jiǎn)單總結(jié)一下

"VSP-mode"為1,是用采樣數(shù)據(jù)中清空前吟榴,最新的時(shí)間戳nextvsync
"VSP-mode"為0魁蒜,是用采樣數(shù)據(jù)中最老的時(shí)間戳配合上擬合的模型算nextvsync
這個(gè)采樣的數(shù)據(jù)會(huì)在hw vsync或者present fence signal后被加入進(jìn)來(lái),采樣的數(shù)據(jù)保留最新的6個(gè)吩翻,重新計(jì)算模型兜看。

我通過(guò)trace,也驗(yàn)證了這個(gè)事情狭瞎,開(kāi)始硬件vsync的第一個(gè)nextvsync是"VSP-mode"為1的方式算出來(lái)的细移。


3.3 app vsync和next vsync的關(guān)系

我之前以為next vsync就是app vsync,其實(shí)大錯(cuò)特錯(cuò)熊锭,看這段代碼

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;
 ···
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
     return getExpectedCallbackTime(nextVsyncTime, timing);
 }

這里有一個(gè)重要的結(jié)構(gòu)體ArmedInfo弧轧,解讀一下。

    struct ArmingInfo {
        nsecs_t mActualWakeupTime;
        nsecs_t mActualVsyncTime;
        nsecs_t mActualReadyTime;
    };
app vsync的ArmingInfo

nextWakeupTime:下一個(gè)app vsync觸發(fā)時(shí)機(jī)碗殷,觸發(fā)app繪制
nextVsyncTime:nextWakeupTime觸發(fā)app繪制精绎,最后送顯屏幕的時(shí)間。
nextReadyTime:nextWakeupTime觸發(fā)app繪制完成锌妻,觸發(fā)sf合成的時(shí)間代乃。

sf vsync的ArmingInfo

nextWakeupTime:下一個(gè)sf vsync觸發(fā)時(shí)機(jī),觸發(fā)sf合成
nextVsyncTime:nextWakeupTime觸發(fā)sf合成仿粹,最后送顯屏幕的時(shí)間搁吓。
nextReadyTime:等同于nextVsyncTime。

雖然app request和sf request next vsync不在同一時(shí)間吭历,但是得到的nextVsyncTime其實(shí)是同一個(gè)堕仔,因?yàn)閮烧咦詈笏惋@時(shí)間是一樣的。


3.4 意外的問(wèn)題分析

我抓trace的發(fā)現(xiàn)一個(gè)很奇怪的事情晌区,就是明明按照[070]一文帶你看懂Vsync Phase相位差計(jì)算摩骨,我的設(shè)備sf和app的vsync不應(yīng)該有相位差,但是我發(fā)現(xiàn)我的設(shè)備一直保持有100us左右的間隔契讲。
仔細(xì)分析trace,原來(lái)是因?yàn)門(mén)imeDispatcher中對(duì)SF vsync的觸發(fā)的callback滑频,過(guò)于耗時(shí)捡偏,導(dǎo)致delay了app vsync的觸發(fā),然后產(chǎn)生了輕微的offset峡迷。

四银伟、總結(jié)

總算是把SurfaceFlinger模塊-VSYNC研究所講的全部消化了你虹,當(dāng)然也發(fā)現(xiàn)文章很多說(shuō)的不是很到位的地方,因?yàn)檎_理解了ArmingInfo 彤避,對(duì)之前自己寫(xiě)的文章[070]一文帶你看懂Vsync Phase有了更加深刻的理解傅物,最后還是要感謝努比亞團(tuán)隊(duì)的文章,終于可以在大腦中形成vsync完整工作流程琉预,哈哈董饰。

白話版Vsync的理解

1.首先采樣的數(shù)據(jù),只有兩個(gè)來(lái)源圆米,hw vsync和present fence卒暂,采樣的數(shù)據(jù)只會(huì)保留最近的6個(gè)hw vsync 時(shí)間戳。
2.根據(jù)采樣的數(shù)據(jù)訓(xùn)練出模型參數(shù)娄帖。
3.然后把std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration))帶入模型就可以獲得下一個(gè)nextvsync
4.然后nextvsync - workDuration - readyDuration就是wakeuptime
5.我的設(shè)備app workDuration = 16.6 readyDuration = 15.6 
6.wakeuptime就是真的app vsync觸發(fā)的時(shí)間也祠,設(shè)置到timedispatcher里。


對(duì)于sf近速,步驟一樣诈嘿,只是workDuration和readyDuration改了
5.我的設(shè)備sf workDuration = 15.6 readyDuration = 0
6.wakeuptime就是真的sf vsync觸發(fā)的時(shí)間,設(shè)置到timedispatcher里削葱。

如果上時(shí)間界面不更新的話奖亚,因?yàn)槌^(guò)750ms,首先會(huì)觸發(fā)硬件采樣的vsync佩耳,同時(shí)清空采樣的數(shù)據(jù)遂蛀,
然后保存采樣數(shù)據(jù)中的最大值mKnownTimestamp ,這時(shí)候就會(huì)走vsp-mode 1干厚,會(huì)使用mKnownTimestamp來(lái)算nextvsync
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末李滴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蛮瞄,更是在濱河造成了極大的恐慌所坯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挂捅,死亡現(xiàn)場(chǎng)離奇詭異芹助,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)闲先,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)状土,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伺糠,你說(shuō)我怎么就攤上這事蒙谓。” “怎么了训桶?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我位岔,道長(zhǎng),這世上最難降的妖魔是什么躁锡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮置侍,結(jié)果婚禮上映之,老公的妹妹穿的比我還像新娘。我一直安慰自己墅垮,他們只是感情好惕医,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著算色,像睡著了一般抬伺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灾梦,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天峡钓,我揣著相機(jī)與錄音,去河邊找鬼若河。 笑死能岩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萧福。 我是一名探鬼主播拉鹃,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鲫忍!你這毒婦竟也來(lái)了膏燕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悟民,失蹤者是張志新(化名)和其女友劉穎坝辫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體射亏,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡近忙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了智润。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片及舍。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖窟绷,靈堂內(nèi)的尸體忽然破棺而出锯玛,到底是詐尸還是另有隱情,我是刑警寧澤钾麸,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布更振,位于F島的核電站,受9級(jí)特大地震影響饭尝,放射性物質(zhì)發(fā)生泄漏肯腕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一钥平、第九天 我趴在偏房一處隱蔽的房頂上張望实撒。 院中可真熱鬧,春花似錦涉瘾、人聲如沸知态。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)负敏。三九已至,卻和暖如春秘蛇,著一層夾襖步出監(jiān)牢的瞬間其做,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工赁还, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妖泄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓艘策,卻偏偏與公主長(zhǎng)得像蹈胡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子朋蔫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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