1. 程式人生 > >android vsnc 工作原理

android vsnc 工作原理



我們知道Android是用Vsync來驅動系統的畫面更新包括APPview draw ,surfaceflinger 畫面的合成,displaysurfaceflinger合成的畫面呈現在LCD.我們將在本文探討AndroidVsync的實現.

Vsync的構成

systrace,我們經常可以看到如上圖的資訊.

  • 紅色框1是與Vsync相關event資訊.這些Vsync event構成了android系統畫面更新基礎.

  • 紅色框2和紅色框3Vsync-app的資訊.我們可以看到紅色框2比紅色框3稀疏.我們將會在本文說明其原因

    .

  • 紅色框4和紅色框5Vsync-sf的資訊.我們可以看到紅色框4比紅色框5稀疏.我們將會在本文說明其原因.

Vsync訊號

Vsync訊號由HW_VSYNC_ON_0,HW_VSYNC_0, Vsync-appVsync-sf四部分構成.

  • HW_VSYNC_ON_0

    代表PrimaryDisplayVSyncenabledisable.0這個數字代表的是display的編號, 0PrimaryDisplay,如果是Externalmonitor,就會是HW_VSYNC_ON_1.SF要求HWComposerDisplay

    VSync開啟或關掉時,這個event就會記錄下來.

  • HW_VSYNC_0

    代表PrimaryDisplayVSync發生時間點, 0同樣代表display編號.其用來調節Vsync-appVsync-sfevent的輸出.

  • Vsync-app

    App,SystemUIsystemserver viewdraw的觸發器.

  • Vsync-sf

    Surfaceflinger合成畫面的觸發器.

通常為了避免Tearing的現象,畫面更新(Flip)的動作通常會在VSync開始的時候才做,因為在VSync開始到它結束前, Display

不會把framebuffer資料顯示在display,所以在這段時間做Flip可以避免使用者同時看到前後兩個部份畫面的現象.目前user看到畫面呈現的過程是這樣的,app更新它的畫面後,它需要透過BufferQueue通知SF, SF再將更新過的app畫面與其它的AppSystemUI組合後,再顯示在User面前.在這個過程裡,3component牽涉進來,分別是App,SF,Display.以目前AndroidM的設計,這三個Component都是在VSync發生的時候才開始做事.我們將它們想成一個有3stagepipeline,這個pipelineclockLCDTE訊號(60HZ)也即HW_VSYNC_0.

我們來看看android drawpipeline.如下,

 

1. T = 0, App正在畫N, SFDisplay都沒內容可用

2. T = 1, App正在畫N+1, SF組合N, DisplayBuffer可顯示

3. T = 2, App正在畫N+2, SF組合N+1, Display顯示N

4. T = 3, App正在畫N, SF組合N+2, Display顯示N+1

5. ...

如果按照這個步驟,user改變一個畫面時,要等到2VSync,畫面才會顯示在user面前, latency大概是33ms (2frame的時間).但是對大部份的操作來講,可能appSF畫的時間一個frame(16.6ms)就可以完成.因此, Android就從HW_VSYNC_0中產生出兩個VSync訊號,VSYNC-app是給App用的, VSYNC-sf是給SF用的, Display則是使用HW_VSYNC_0.VSYNC-appVSYNC-sf可以分別給定一個phase,簡單的說

VSYNC-app = HW_VSYNC_0 + phase_app

VSYNC-sf =HW_VSYNC_0 + phase_sf

從而使App drawsurfaceflinger的合成,錯開時間執行.這樣就有可能整個系統drawpipeline更加有效率,從而提高使用者體驗.

也就是說,如果phase_appphase_sf設定的好的話,可能大部份user使用的狀況, App+SF可以在一個frame裡完成,然後在下一個HW_VSYNC_0來的時候,顯示在display.

理論上透過VSYNC-sfVSYNC-app的確是可以有機會在一個frame裡把App+SF做完,但是實際上不是一定可以在一個frame裡完成.因為有可能因為CPU排程,GPUperformance不夠,以致AppSF沒辦法及時做完.但是即便如此,app,surfaceflingerdisplayVsync分開也比用一個Vsynctrigger appdraw,surfaceflinger合成,displayLCDdraw的效能要好很多.(FPS更高).

那麼是否我們收到LCDVsyncevent就會觸發Vsync-appVsync-SF.如果是這樣我們就不會看到本文開頭的Vsync-appVsync-SF的節奏不一致的情況(紅色框2比紅色框3稀疏).事實上,在大多數情況下,APP的畫面並不需要一直更新.比如我們看一篇文章,大部份時間,畫面是維持一樣的,如果我們在每個LCDVSYNC來的時候都觸發SFAPP,就會浪費時間和Power.可是在畫面持續更新的情況下,我們又需要觸發SFAppVsync event.例如玩game或播影片時.就是說我們需要根據是否有內容的更新來選擇Vsyncevent出現的機制.Vsync event 的觸發需要按需出現.所以在Android, HW_VSYNC_0,Vsync-sfVsync-app都會按需出現. HW_VSYNC_0是根據是否需要調節sfappVsyncevent而出現,SFApp則會call requestNextVsync()來告訴系統我要在下一個VSYNC需要被trigger.也是雖然系統每秒有60HW VSYNC,但不代表APPSF在每個VSYNC都要更新畫面.因此,Android,是根據SoftwareVSYNC(Vsync-sf,Vsync-app)來更新畫面.Software VSYNC是根據HWVSYNC過去發生的時間,推測未來會發生的時間.因此,APPSF利用requestNextVsync,Software VSYNC才會觸發VSYNC-sfVSYNC-app.

Vsync event的產生

下圖是Vsyncevent產生的示意圖.

這裡HW_VSYNC就是HW_VSYNC_0.

SFHWComposer收到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_VSYNCPresentFence來判斷是否需要開啟HW_VSYNC.HW_VSYNC最少要3,最多是32,實際上要用幾個則不一定,DispSync拿到3HW_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),如果AvgPeriod360,DeltaPhase = 2*PI*Delta/AvgPeriod.

  • DeltaPhase可以得到DeltaXDeltaY (DeltaX =cos(DeltaPhase), DeltaY = sin(DeltaPhase))

  • 將每個收到的VSYNCDeltaXDeltaY取平均,可以得到AvgXAvgY

  • 利用atanAvgX, AvgY可以得到平圴的phase (AvgPhase)

  • AvgPeriod + AvgPhase就是SW_VSYNC.

DispSync收到addPresentFence(最多記錄8sample),每一個fence的時間算出(Time% AvgPeriod)的平方當作誤差,將所有的Fence