android vsnc 工作原理
我們知道Android是用Vsync來驅動系統的畫面更新包括APPview draw ,surfaceflinger 畫面的合成,display把surfaceflinger合成的畫面呈現在LCD上.我們將在本文探討Android的Vsync的實現.
Vsync的構成
在systrace中,我們經常可以看到如上圖的資訊.
-
紅色框1是與Vsync相關event資訊.這些Vsync event構成了android系統畫面更新基礎.
-
紅色框2和紅色框3是Vsync-app的資訊.我們可以看到紅色框2比紅色框3稀疏.我們將會在本文說明其原因
-
紅色框4和紅色框5是Vsync-sf的資訊.我們可以看到紅色框4比紅色框5稀疏.我們將會在本文說明其原因.
Vsync訊號
Vsync訊號由HW_VSYNC_ON_0,HW_VSYNC_0, Vsync-app和Vsync-sf四部分構成.
-
HW_VSYNC_ON_0
代表PrimaryDisplay的VSync被enable或disable.0這個數字代表的是display的編號, 0是PrimaryDisplay,如果是Externalmonitor,就會是HW_VSYNC_ON_1.當SF要求HWComposer將Display的
-
HW_VSYNC_0
代表PrimaryDisplay的VSync發生時間點, 0同樣代表display編號.其用來調節Vsync-app和Vsync-sfevent的輸出.
-
Vsync-app
App,SystemUI和systemserver 等viewdraw的觸發器.
-
Vsync-sf
Surfaceflinger合成畫面的觸發器.
通常為了避免Tearing的現象,畫面更新(Flip)的動作通常會在VSync開始的時候才做,因為在VSync開始到它結束前, Display
我們來看看android draw的pipeline.如下,
1. T = 0時, App正在畫N, SF與Display都沒內容可用
2. T = 1時, App正在畫N+1, SF組合N, Display沒Buffer可顯示
3. T = 2時, App正在畫N+2, SF組合N+1, Display顯示N
4. T = 3時, App正在畫N, SF組合N+2, Display顯示N+1
5. ...
如果按照這個步驟,當user改變一個畫面時,要等到2個VSync後,畫面才會顯示在user面前, latency大概是33ms (2個frame的時間).但是對大部份的操作來講,可能app加SF畫的時間一個frame(16.6ms)就可以完成.因此, Android就從HW_VSYNC_0中產生出兩個VSync訊號,VSYNC-app是給App用的, VSYNC-sf是給SF用的, Display則是使用HW_VSYNC_0.VSYNC-app與VSYNC-sf可以分別給定一個phase,簡單的說
VSYNC-app = HW_VSYNC_0 + phase_app
VSYNC-sf =HW_VSYNC_0 + phase_sf
從而使App draw和surfaceflinger的合成,錯開時間執行.這樣就有可能整個系統draw的pipeline更加有效率,從而提高使用者體驗.
也就是說,如果phase_app與phase_sf設定的好的話,可能大部份user使用的狀況, App+SF可以在一個frame裡完成,然後在下一個HW_VSYNC_0來的時候,顯示在display上.
理論上透過VSYNC-sf與VSYNC-app的確是可以有機會在一個frame裡把App+SF做完,但是實際上不是一定可以在一個frame裡完成.因為有可能因為CPU排程,GPUperformance不夠,以致App或SF沒辦法及時做完.但是即便如此,把app,surfaceflinger和displayVsync分開也比用一個Vsync來trigger appdraw,surfaceflinger合成,display在LCD上draw的效能要好很多.(FPS更高).
那麼是否我們收到LCD的Vsyncevent就會觸發Vsync-app和Vsync-SF呢.如果是這樣我們就不會看到本文開頭的Vsync-app和Vsync-SF的節奏不一致的情況(紅色框2比紅色框3稀疏).事實上,在大多數情況下,APP的畫面並不需要一直更新.比如我們看一篇文章,大部份時間,畫面是維持一樣的,如果我們在每個LCD的VSYNC來的時候都觸發SF或APP,就會浪費時間和Power.可是在畫面持續更新的情況下,我們又需要觸發SF和App的Vsync event.例如玩game或播影片時.就是說我們需要根據是否有內容的更新來選擇Vsyncevent出現的機制.Vsync event 的觸發需要按需出現.所以在Android裡, HW_VSYNC_0,Vsync-sf和Vsync-app都會按需出現. HW_VSYNC_0是根據是否需要調節sf和app的Vsyncevent而出現,而SF和App則會call requestNextVsync()來告訴系統我要在下一個VSYNC需要被trigger.也是雖然系統每秒有60個HW VSYNC,但不代表APP和SF在每個VSYNC都要更新畫面.因此,在Android裡,是根據SoftwareVSYNC(Vsync-sf,Vsync-app)來更新畫面.Software VSYNC是根據HWVSYNC過去發生的時間,推測未來會發生的時間.因此,當APP或SF利用requestNextVsync時,Software VSYNC才會觸發VSYNC-sf或VSYNC-app.
Vsync event的產生
下圖是Vsyncevent產生的示意圖.
這裡HW_VSYNC就是HW_VSYNC_0.
當SF從HWComposer收到VSYNC(HW_VSYNC_0)時,它會利用DispSync::addResyncSample將新的VSYNC時間交給DispSync.addResyncSample決定是否還需要HW_VSYNC的輸入,如果不需要,就會將HW_VSYNC關掉.
voidSurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
bool needsHwVsync = false;
{ // Scope for the lock
Mutex::Autolock _l(mHWVsyncLock);
if (type == 0 &&mPrimaryHWVsyncEnabled) {
needsHwVsync =mPrimaryDispSync.addResyncSample(timestamp);
}
}
if (needsHwVsync) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
另一方面,在sufaceflinge合成圖片後也會check是否需要開啟HW_VSYNC來調整SW_VSYNC.
在SurfaceFlinger::postComposition()裡,會將PresentFence的時間通過addPresentFence交給DispSync,來檢查SW_VSYNC是否需要校正,如果需要,就會將HW_VSYNC開啟.
voidSurfaceFlinger::postComposition()
{
const LayerVector&layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
layers[i]->onPostComposition();
}
const HWComposer& hwc =getHwComposer();
sp<Fence> presentFence =hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
if(mPrimaryDispSync.addPresentFence(presentFence)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
const sp<const DisplayDevice>hw(getDefaultDisplayDevice());
if (kIgnorePresentFences) {
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
…..
}
DispSync是利用HW_VSYNC和PresentFence來判斷是否需要開啟HW_VSYNC.HW_VSYNC最少要3個,最多是32個,實際上要用幾個則不一定,DispSync拿到3個HW_VSYNC後就會計算出SW_VSYNC,只要收到的PresentFence沒有超過誤差,則HW_VSYNC就會關掉,以便節省功耗.不然會繼續開啟HW_VSYNC計算SW_VSYNC的值,直到誤差小於threshold.其計算的方法是DispSync::updateModelLocked().
基本思想如下,
-
計算目前收到HW_VSYNC間隔,取平均值(AvgPeriod) HW_VSYNC
-
將每個收到的VSYNC時間與AvgPeriod算出誤差. (Delta = Time %AvgPeriod)
-
將Delta轉換成角度(DeltaPhase),如果AvgPeriod是360度,DeltaPhase = 2*PI*Delta/AvgPeriod.
-
從DeltaPhase可以得到DeltaX與DeltaY (DeltaX =cos(DeltaPhase), DeltaY = sin(DeltaPhase))
-
將每個收到的VSYNC的DeltaX與DeltaY取平均,可以得到AvgX與AvgY
-
利用atan與AvgX, AvgY可以得到平圴的phase (AvgPhase)
-
AvgPeriod + AvgPhase就是SW_VSYNC.
當DispSync收到addPresentFence時(最多記錄8個sample),每一個fence的時間算出(Time%
AvgPeriod)的平方當作誤差,將所有的Fence