[085]SW VSYNC模型更新與校準(zhǔn)

背景

對(duì)于Vsync信號(hào)的相關(guān)領(lǐng)域中,一直有一座大山我一直沒(méi)有翻躍,那就是SW VSYNC模型更新與校準(zhǔn)。
經(jīng)過(guò)認(rèn)真閱讀努比亞技術(shù)團(tuán)隊(duì)的文章SurfaceFlinger模塊-VSYNC研究,我終于翻躍了這座大山。
本文是針對(duì)SW VSYNC模型更新與校準(zhǔn)這部分的理解和補(bǔ)充,建議先看努比亞的文章再看我這個(gè)文章。

1.根據(jù)采樣的時(shí)間戳更新模型

對(duì)應(yīng)SurfaceFlinger模塊-VSYNC研究文章中3.2 1 addVsyncTimestamp章節(jié)。
通過(guò)著6個(gè)樣本,計(jì)算出x的因變量集合 ordinals,和y的自變量集合vsyncTS。通過(guò)6個(gè)樣本把這兩個(gè)集合的數(shù)據(jù)都計(jì)算出來(lái),然后通過(guò)上面的方程式把回歸系數(shù)和截距都計(jì)算出來(lái),這塊的回歸系數(shù)就是Vsync的時(shí)間周期,前面我加過(guò)日志,我把這兩個(gè)集合的內(nèi)容可以貼出來(lái)看下,以下是90fps的vsync信息。
x的集合內(nèi)容 {0,1000,2000,3000,4000,5000} ,從集合的內(nèi)容是vsync的個(gè)數(shù)信息。
y的集合內(nèi)容{0,11027000,22053000,33080000,44106000,55132000},從代碼中了解是硬件vsync時(shí)間戳的遞增值,

看了有點(diǎn)蒙圈,假設(shè)傳入連續(xù)的六個(gè)時(shí)間戳,t是為任意值。

{t,t + 11027000,t + 22053000,t + 33080000,t + 44106000,t + 55132000}

我們得到的其實(shí)是集合映射關(guān)系是M~T,m代表第m個(gè)vsync信號(hào),t就是第m個(gè)信號(hào)時(shí)間戳,單位納秒,也就是第m個(gè)vsync時(shí)間戳為t納秒,第m+1個(gè)vsync時(shí)間戳為t + 11027000納秒

M {m, m+1, m+2, m+3, m+4, m+5} ~ T {t,t + 11027000,t + 22053000,t + 33080000,t + 44106000,t + 55132000}

這個(gè)M-T映射關(guān)系和上面努比亞文章中的x-y集合映射有什么關(guān)系呢?

x{0,1000,2000,3000,4000,5000} ~ y {0,11027000,22053000,33080000,44106000,55132000}

我們看代碼

// normalizing to the oldest timestamp cuts down on error in calculating the intercept.
auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
vsyncTS[i] = mTimestamps[i] - oldest_ts;

oldest_ts 就是T的集合中時(shí)間戳最小值,也就是t。每個(gè)時(shí)間戳在減去t,那不就得到了y集合。

y {0,11027000,22053000,33080000,44106000,55132000}

currentPeriod 在90hz屏幕下固定值為11111111,vsyncTS[i] 就是y集合中數(shù)字,其中kScalingFactor 放大因子為1000, 根據(jù)以下公式,可以得到x的集合

static constexpr int64_t kScalingFactor = 1000;
ordinals[i] = ((vsyncTS[i] + (currentPeriod / 2)) / currentPeriod) * kScalingFactor;

所以我們根據(jù)傳入的六個(gè)連續(xù)vsync時(shí)間戳得到了努比亞文章中所描述的X和Y集合映射。

{t,t + 11027000,t + 22053000,t + 33080000,t + 44106000,t + 55132000}
得到了
x {0,1000,2000,3000,4000,5000}  ~ y {0,11027000,22053000,33080000,44106000,55132000}

假設(shè)y=bx + a是擬合x(chóng)~y映射關(guān)系的函數(shù),用以下公式來(lái)就先求中b的值
b = slope = top/bottom。

     //         Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )      -----> top
     // slope = -------------------------------------------
     //         Sigma_i ( X_i - mean(X) ) ^ 2                  -----> bottom

假設(shè)x=1000x',如何求y=b'x' + a'的函數(shù)曲線(xiàn)?

直接將1000x'帶入y=bx + a中即可得y=b*1000x' + a
所以b' = 1000b, a' = a , 其中b'也就是下面這段代碼中anticipatedPeriod = 1000 * slope = 1000 * top/bottom。

nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;//kScalingFactor  = 1000

因?yàn)閍' = a,所以只要按照以下公式求得a,也就是intercept 就好了

     //
     // intercept = mean(Y) - slope * mean(X)
     //

但是由于anticipatedPeriod = 1000 * slope,所以下面需要除以kScalingFactor(1000)才能得到正確的intercept,也就是a'。

nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);

小結(jié)

通過(guò)轉(zhuǎn)化,將集合T轉(zhuǎn)化成了x-y的映射關(guān)系

T {t,t + 11027000,t + 22053000,t + 33080000,t + 44106000,t + 55132000}
經(jīng)過(guò)轉(zhuǎn)化得到
x{0,1000,2000,3000,4000,5000} ~ y {0,11027000,22053000,33080000,44106000,55132000}

根據(jù)x-y的映射關(guān)系,算出了y=b'x' + a'中b'和a'

b' = anticipatedPeriod
a' = intercept

因?yàn)閤=1000x',所以 y=b'x' + a'描述以下x'-y的映射關(guān)系的直線(xiàn),所以努比亞文章中描述有點(diǎn)不準(zhǔn)確。

x' {0,1,2,3,4,5}  ~ y {0,11027000,22053000,33080000,44106000,55132000}

為什么要加入1000的縮放因子,應(yīng)該是為了提高計(jì)算的精準(zhǔn)度。

二、已知上一次的vsync的時(shí)間戳求next vsync的時(shí)間戳

目前我們已經(jīng)通過(guò)前面得到了一個(gè)函數(shù)y=b'x' + a'來(lái)表示以下集合的映射關(guān)系,其中b' = slope,a' = intercept

x' {0,1,2,3,4,5}  ~ y {0,11027000,22053000,33080000,44106000,55132000}

根據(jù)模型獲得slope斜率,以及截距intercept,用圖表示如下。

auto const [slope, intercept] = getVSyncPredictionModelLocked();

假設(shè)我們上一次的vsync時(shí)間戳為T(mén)ime1,如果求得下一個(gè)vsync的時(shí)間戳Time2呢?

努比亞團(tuán)隊(duì)是這樣子描述的,有點(diǎn)不是很清楚。
如果mTimestamps的集合不為空,通過(guò)這個(gè)集合的數(shù)據(jù)和傳入的發(fā)射時(shí)間,算出一次線(xiàn)程回歸方式的因變量x值,然后根據(jù)回歸系數(shù)和截距,用方程式計(jì)算出自變量y值,而y值,也就是代碼中的prediction,作為下一次vsync發(fā)射的時(shí)間。

以下代碼描述了這個(gè)計(jì)算過(guò)程

    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;

用圖來(lái)簡(jiǎn)單表示一下,可以好好思考一下,初中數(shù)學(xué)知識(shí)應(yīng)該就就可以看得懂上述的計(jì)算方式。
其中A=timePoint - zeroPoint


三、總結(jié)

終于把SW VSYNC模型更新與校準(zhǔn)這座大山翻過(guò)去了,感謝努比亞團(tuán)隊(duì),隨便其中部分描述存在讓我看不懂的情況,努力思考,看代碼,最后把整個(gè)邏輯看明白了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容