Android 4.4(KitKat)中VSync信號的虛擬化

 

Android 4.1(Jelly Bean)引入瞭Vsync(Vertical Syncronization)用於渲染同步,使得App UI和SurfaceFlinger可以按硬件產生的VSync節奏來進行工作。關於VSync的介紹詳見博文https://www.androidpolice.com/2012/07/12/getting-to-know-android-4-1-part-3-project-butter-how-it-works-and-what-it-added/。引用該博文的一張圖來作為本文的開頭:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20131213/201312130914459.png

雖然大傢都同步瞭,但新的問題產生瞭:

1. App UI和SurfaceFlinger的工作顯然是一個流水線的模型。即對於一幀內容,先等App UI畫完瞭,SurfaceFlinger再出場對其進行合並渲染後放入framebuffer,最後整到屏幕上。而現有的VSync模型是讓大傢一起開始幹活。這樣對於同一幀內容,第一個VSync信號時App UI的數據開始準備,第二個VSync信號時SurfaceFlinger工作,第三個VSync信號時用戶看到內容,這樣就兩個VSync period(每個16ms)過去瞭。這會影響用戶體驗。

2.計算機資源是有限的,大傢一起做事,都搶資源,必然導致工作加倍的慢。

 

Android 4.4(KitKat)引入瞭VSync的虛擬化,即把硬件的VSync信號先同步到一個本地VSync模型中,再從中一分為二,引出兩條VSync時間與之有固定偏移的線程。示意圖如下:

 

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20131213/2013121309144512.jpg

 

這樣,大傢工作既保持一定的節拍,又可以相互錯開,一前一後保持著咚次噠次,咚次噠次的流水節奏瞭:) 註意其中兩個Phase offset參數(即VSYNC_EVENT_PHASE_OFFSET_NS和SF_VSYNC_EVENT_PHASE_OFFSET_NS)是可調的。

 

相關的主要創建流程在DispSync::DispSync():

292DispSync::DispSync() {
293    mThread = new DispSyncThread();
294    mThread->run(DispSync, PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);

 

以及SurfaceFlinger::init()中:

604    sp vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
605            vsyncPhaseOffsetNs, true);
606    mEventThread = new EventThread(vsyncSrc);
607    sp sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
608            sfVsyncPhaseOffsetNs, false);
609    mSFEventThread = new EventThread(sfVsyncSrc);
610    mEventQueue.setEventThread(mSFEventThread);
611
612    mEventControlThread = new EventControlThread(this);
613    mEventControlThread->run(EventControl, PRIORITY_URGENT_DISPLAY);

相關的類圖如下:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20131213/2013121309144513.jpg

如果上圖不怎麼直觀的話,下面這張用序列圖表示的線程模型圖應該更好理解一點:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20131213/2013121309144614.jpg

類型DispSync表示瞭一個基於硬件VSync信號的同步模型,它會根據從HWComposer來的硬件VSync信號的采樣來進行同步。其它兩個EventThread分別用瞭兩個不同的虛擬VSync信號源(用DispSyncSource表示,其中包含瞭與真實VSync信號的偏移值),這兩個VSync信號源就是被虛擬出來分別用於控制App UI和SurfaceFlinger渲染的。在EventThread的線程循環中,如果有需要就會向DispSync註冊相應的listener。DispSyncThread就像樂隊鼓手一樣控制著大傢的節奏。它在主循環中會先通過已經向DispSync註冊的listener計算下一個要產生的虛擬VSync信號還要多久,等待相應時間後就會調用相應listener的callback函數。這樣,對於那些註冊瞭listener的監聽者來說,就好像被真實的VSync信號控制著一樣。至於EventControlThread是用來向真實的VSync硬件發命令的,可以先不管。

 

看來谷歌對於大傢對Android UI流暢性的吐槽也已經很鬱悶瞭,才會把VSync整這麼麻煩。不過這樣做缺點是引入瞭需要tune的參數,如果設不好結果可能也好不到哪去,將來會不會通過一些統計手段來讓這兩個參數自適應呢?

 

發佈留言