轉(zhuǎn)自:http://blog.csdn.NET/yangwen123/article/details/16344375 也可參考:http://djt.qq.com/article/view/987
進(jìn)程間通訊機(jī)制 Android應(yīng)用程序?yàn)榱四軌驅(qū)⒆约旱腢I繪制在系統(tǒng)的幀緩沖區(qū)上,它們就必須要與SurfaceFlinger服務(wù)進(jìn)行通信,如圖所示:
Android應(yīng)用程序與SurfaceFlinger服務(wù)是運(yùn)行在不同的進(jìn)程中的,因此,它們采用某種進(jìn)程間通信機(jī)制來進(jìn)行通信。由于Android應(yīng)用程序在通知SurfaceFlinger服務(wù)來繪制自己的UI的時(shí)候,需要將UI數(shù)據(jù)傳遞給SurfaceFlinger服務(wù),例如,要繪制UI的區(qū)域、位置等信息。一個(gè)Android應(yīng)用程序可能會有很多個(gè)窗口,而每一個(gè)窗口都有自己的UI數(shù)據(jù),因此,Android系統(tǒng)的匿名共享內(nèi)存機(jī)制就派上用場了。 每一個(gè)Android應(yīng)用程序與SurfaceFlinger服務(wù)之間,都會通過一塊匿名共享內(nèi)存來傳遞UI數(shù)據(jù),如下所示:
但是單純的匿名共享內(nèi)存在傳遞多個(gè)窗口數(shù)據(jù)時(shí)缺乏有效的管理,所以匿名共享內(nèi)存就被抽象為一個(gè)更上流的數(shù)據(jù)結(jié)構(gòu)SharedClient,如下圖所示:
在每個(gè)SharedClient中,最多有31個(gè)SharedBufferStack,每個(gè)SharedBufferStack都對應(yīng)一個(gè)Surface,即一個(gè)窗口。這樣,我們就可以知道為什么每一個(gè)SharedClient里面包含的是一系列SharedBufferStack而不是單個(gè)SharedBufferStack:一個(gè)SharedClient對應(yīng)一個(gè)Android應(yīng)用程序,而一個(gè)Android應(yīng)用程序可能包含有多個(gè)窗口,即Surface。從這里也可以看出,一個(gè)Android應(yīng)用程序至多可以包含31個(gè)窗口。 每個(gè)SharedBufferStack中又包含了N個(gè)緩沖區(qū)(<4.1 N=2; >=4.1 N=3),即顯示刷新機(jī)制中即將提到的雙緩沖和三重緩沖技術(shù)。
顯示刷新機(jī)制 一般我們在繪制UI的時(shí)候,都會采用一種稱為“雙緩沖”的技術(shù)。雙緩沖意味著要使用兩個(gè)緩沖區(qū)(SharedBufferStack中),其中一個(gè)稱為Front Buffer,另外一個(gè)稱為Back Buffer。UI總是先在Back Buffer中繪制,然后再和Front Buffer交換,渲染到顯示設(shè)備中。理想情況下,這樣一個(gè)刷新會在16ms內(nèi)完成(60FPS),下圖就是描述的這樣一個(gè)刷新過程(Display處理前Front Buffer,CPU、GPU處理Back Buffer。
隨著時(shí)間的推移,Android OS系統(tǒng)一直在不斷完善。但直到Android 4.0問世,有關(guān)UI顯示不流暢的問題也一直未得到根本解決。在整個(gè)進(jìn)化過程中,Android在顯示系統(tǒng)這塊也下了不少功夫,例如,使用硬件加速等技術(shù),但本質(zhì)原因似乎和硬件關(guān)系并不大,因?yàn)閕Phone的硬件配置并不比那些價(jià)格相近的Android機(jī)器的硬件配置強(qiáng),而iPhone UI的流暢性強(qiáng)卻是有目共睹的。從Android 4.1(版本代號為Jelly Bean)開始,Android OS開發(fā)團(tuán)隊(duì)便力圖在每個(gè)版本中解決一個(gè)重要問題。作為嚴(yán)重影響Android口碑問題之一的UI流暢性差的問題,首先在Android 4.1版本中得到了有效處理。其解決方法就是本文要介紹的Project Butter。Project Butter對Android Display系統(tǒng)進(jìn)行了重構(gòu),引入了三個(gè)核心元素,即VSYNC、Triple Buffer和Choreographer。其中,VSYNC是理解Project Buffer的核心。VSYNC是Vertical Synchronization(垂直同步)的縮寫,是一種在PC上已經(jīng)很早就廣泛使用的技術(shù)。讀者可簡單的把它認(rèn)為是一種定時(shí)中斷。接下來,本文將圍繞VSYNC來介紹Android Display系統(tǒng)的工作方式。 Android系統(tǒng)UI交互滯后問題
在一個(gè)典型的顯示系統(tǒng)中,frame buffer代表了屏幕即將要顯示的一幀畫面。假如CPU/GPU繪圖過程與屏幕刷新所使用的buffer是同一塊,那么當(dāng)它們的速度不同步的時(shí)候,是很可能出現(xiàn)類似的畫面“割裂”的。舉個(gè)具體的例子來說,假設(shè)顯示器的刷新率為66Hz,而CPU/GPU繪圖能力則達(dá)到100Hz,也就是它們處理完成一幀數(shù)據(jù)分別需要0.015秒和0.01秒。
0.01秒時(shí),由于兩者速率相差不小,此時(shí)buffer中已經(jīng)準(zhǔn)備好了第1幀數(shù)據(jù),顯示器只顯示了第1幀畫面的2/3
0.015秒時(shí),第1幀畫面完整地顯示出來了,此時(shí)buffer中有1/3的部分已經(jīng)被填充上第2幀數(shù)據(jù)了 0.02秒時(shí),Buffer中已經(jīng)準(zhǔn)備好第2幀數(shù)據(jù),而顯示屏出現(xiàn)了screen tearing,有三分之一是第2幀內(nèi)容,其余的則屬于第1幀畫面
在單緩沖區(qū)的情況下,這個(gè)問題很難規(guī)避。所以引進(jìn)了雙緩沖技術(shù),基本原理就是采用兩塊buffer。一塊back buffer用于CPU/GPU后臺繪制,另一塊framebuffer則用于顯示,當(dāng)back buffer準(zhǔn)備就緒后,它們才進(jìn)行交換。那么什么時(shí)候切換兩個(gè)緩沖區(qū)最合適呢?顯示器有兩個(gè)重要特性,行頻和場頻。行頻(Horizontal ScanningFrequency)又稱為“水平掃描頻率”,是屏幕每秒鐘從左至右掃描的次數(shù); 場頻(Vertical Scanning Frequency)也稱為“垂直掃描頻率”,是每秒鐘整個(gè)屏幕刷新的次數(shù)。由此也可以得出它們的關(guān)系:
行頻=場頻*縱坐標(biāo)分辨率。 當(dāng)掃描完一個(gè)屏幕后,設(shè)備需要重新回到第一行以進(jìn)入下一次的循環(huán),此時(shí)有一段時(shí)間空隙,稱為VerticalBlanking Interval(VBI)。這個(gè)時(shí)間點(diǎn)就是我們進(jìn)行緩沖區(qū)交換的最佳時(shí)間。因?yàn)榇藭r(shí)屏幕沒有在刷新,也就避免了交換過程中出現(xiàn)screentearing的狀況。VSync是VerticalSynchronization的簡寫,它利用VBI時(shí)期出現(xiàn)的vertical sync pulse來保證雙緩沖在最佳時(shí)間點(diǎn)才進(jìn)行交換。在沒有VSync信號同步下的繪圖過程: 沒有VSYNC的繪圖過程
為解決這個(gè)問題,從Android 4.1Jelly Bean開始,Project Buffer引入了VSYNC,系統(tǒng)在收到VSync pulse后,將馬上開始下一幀的渲染。結(jié)果如下圖所示:
引入VSYNC的繪制過程
每收到VSYNC中斷,CPU就開始處理各幀數(shù)據(jù)。大部分的Android顯示設(shè)備刷新率是60Hz,這也就意味著每一幀最多只能有1/60=16ms左右的準(zhǔn)備時(shí)間。假如CPU/GPU的FPS(FramesPer Second)高于這個(gè)值,那么顯示效果將很好。但是,這時(shí)出現(xiàn)一個(gè)新問題:CPU和GPU處理數(shù)據(jù)的速度都能在16ms內(nèi)完成,而且還有時(shí)間空余,但必須等到VSYNC信號到來后才處理下一幀數(shù)據(jù),因此CPU/GPU的FPS被拉低到與Display的FPS相同。下圖是采用雙緩沖區(qū)的顯示效果:
雙緩沖下CPU/GPU FPS大于刷新頻率 同時(shí)采用了雙緩沖技術(shù)以及VSYNC,可以看到整個(gè)過程還是相當(dāng)不錯(cuò)的,雖然CPU/GPU處理所用的時(shí)間時(shí)短時(shí)長,但總的來說都在16ms以內(nèi),因而不影響顯示效果。A和B分別代表兩個(gè)緩沖區(qū),它們不斷地交換來正確顯示畫面。如果CPU/GPU的FPS小于Display的FPS,會是什么情況呢?
雙緩沖下CPU/GPU FPS小于刷新頻率 當(dāng)CPU/GPU的處理時(shí)間超過16ms時(shí),第一個(gè)VSync到來時(shí),緩沖區(qū)B中的數(shù)據(jù)還沒有準(zhǔn)備好,于是只能繼續(xù)顯示之前A緩沖區(qū)中的內(nèi)容。而B完成后,又因?yàn)槿狈Sync信號,CPU/GPU只能等待下一個(gè)VSync的來臨才開處理下一幀數(shù)據(jù)。于是在這一過程中,有一大段時(shí)間是被浪費(fèi)。當(dāng)下一個(gè)VSync出現(xiàn)時(shí),CPU/GPU馬上執(zhí)行操作,此時(shí)它可操作的buffer是A,相應(yīng)的顯示屏對應(yīng)的就是B。這時(shí)看起來就是正常的。只不過由于執(zhí)行時(shí)間仍然超過16ms,導(dǎo)致下一次應(yīng)該執(zhí)行的緩沖區(qū)交換又被推遲了。也就是說在第二個(gè)16ms時(shí)間段,Display本應(yīng)顯示B幀,但卻因?yàn)镚PU還在處理B幀,導(dǎo)致A幀被重復(fù)顯示,同時(shí)CPU無所事事,因?yàn)锳 被Display在使用。B被GPU在使用。為什么CPU不能在第二個(gè)16ms處即VSync到來就開始工作呢?原因就是只有兩個(gè)Buffer。如果有第三個(gè)Buffer的存在,CPU就可以開始工作,而不至于空閑。出于這一思路就引出了Triple Buffer。結(jié)果如圖所示:
Triple Buffering
第二個(gè)16ms時(shí)間段,CPU使用C Buffer繪圖。雖然剛開始還是會多顯示A幀一次,但后續(xù)顯示效果就比較好,第三個(gè)VSync信號到來時(shí),由于GPU/CPU都處理完了B,因此B被顯示,在第四個(gè)VSync信號到來時(shí),GPU/CPU同時(shí)完成了A和C,并著手開始處理B,此時(shí)C被顯示。從上圖可以看出,CPU繪制的第C幀數(shù)據(jù)要到第四個(gè)16ms才能顯示,這比雙Buffer情況多了16ms延遲。我們知道,應(yīng)用程序這邊的本地窗口Surface在SurfaceFlinger服務(wù)進(jìn)程端有一個(gè)對應(yīng)的BufferQueue對象,該對象用于管理Surface的圖形繪制緩沖區(qū)。BufferQueue中最多有32個(gè)BufferSlot,不過在實(shí)際使用時(shí)具體值是可以設(shè)置的。在Layer對象的onFirstRef函數(shù)中初始化了圖形緩沖區(qū)的個(gè)數(shù):
Project Buffer分析1. 需要VSYNC定時(shí)中斷; 2. 當(dāng)雙Buffer不夠使用時(shí),該系統(tǒng)可分配第三塊Buffer; 3. 圖形buffer的繪制工作又VSYNC信號觸發(fā);
IDisplayEventConnection是一個(gè)純虛類,它代表VSYNC中斷的監(jiān)聽者。其實(shí)體類是EventThread的內(nèi)部類Connection。IDisplayEventConnection定義了一個(gè)getDataChannel函數(shù),該函數(shù)返回一個(gè)BitTube實(shí)例,其內(nèi)部實(shí)現(xiàn)為socketpair。這個(gè)實(shí)例提供的read/write方法,用于傳送具體的信號數(shù)據(jù)。EventThread最重要的一個(gè)VSYNC監(jiān)聽者就是MessageQueue的mEvents對象,來自EventThread的VSYNC中斷信號,將通過MessageQueue轉(zhuǎn)化為一個(gè)REFRESH消息并傳遞給SurfaceFlinger的onMessageReceived函數(shù)處理。DisplayEventReceiver是一個(gè)abstract class,其JNI的代碼部分會創(chuàng)建一個(gè)IDisplayEventConnection的VSYNC監(jiān)聽者對象。這樣,來自EventThread的VSYNC中斷信號就可以傳遞給Choreographer對象了。當(dāng)Choreographer收到VSYNC信號時(shí),就會調(diào)用使用者通過postCallback設(shè)置的回調(diào)函數(shù)。 二 Project Buffer分析上一節(jié)對VSYNC進(jìn)行了理論分析,其實(shí)也引出了Project Buffer的三個(gè)關(guān)鍵點(diǎn):
下面來看Project Buffer實(shí)現(xiàn)的細(xì)節(jié)。 2.1 SurfaceFlinger家族的改進(jìn)首先被動刀的是SurfaceFlinger家族成員。目標(biāo)是提供VSYNC中斷。相關(guān)類圖如圖5所示:
圖5 SurfaceFlinger中和VSYNC有關(guān)的類 由圖5可知:
在SurfaceFlinger家族中,VSyncHandler的實(shí)例是EventThread。下邊是EventThread類的聲明: class EventThread : public Thread, public DisplayHardware::VSyncHandler 由EventThread定義可知,它本身運(yùn)行在一個(gè)單獨(dú)的線程中,并繼承了VSyncHandler。EventThread的核心處理在其線程函數(shù)threadLoop中完成,其處理邏輯主要是:
通過EventThread,VSYNC中斷事件可派發(fā)給多個(gè)該中斷的監(jiān)聽者去處理。相關(guān)類如圖6所示:
圖6 EventThread和VSYNC中斷監(jiān)聽者 由圖6可知:
EventThread最重要的一個(gè)VSYNC監(jiān)聽者就是MessageQueue的mEvents對象。當(dāng)然,這一切都是為最大的后臺老板SurfaceFlinger服務(wù)的。來自EventThread的VSYNC中斷信號,將通過MessageQueue轉(zhuǎn)化為一個(gè)REFRESH消息并傳遞給SurfaceFlinger的onMessageReceived函數(shù)處理。 有必要指出,4.1中SurfaceFlinger onMessageReceived函數(shù)的實(shí)現(xiàn)僅僅是將4.0版本的SurfaceFlinger的核心函數(shù)挪過來罷了[②],并未做什么改動。 以上是Project Buffer對SurfaceFlinger所做的一些改動。那么Triple Buffer是怎么處理的呢?幸好從Android 2.2開始,Display的Page Flip算法就不依賴Buffer的個(gè)數(shù),Buffer個(gè)數(shù)不過是算法的一個(gè)參數(shù)罷了。所以,Triple Buffer的引入,只是把Buffer的數(shù)目改成了3,而算法本身相對于4.0來說并沒有變化。圖7為Triple Buffer的設(shè)置示意圖:
圖7 Layer.cpp中對Triple Buffer的設(shè)置 圖7所示,為Layer.cpp中對Buffer個(gè)數(shù)的設(shè)置。TARGET_DISABLE_TRIPLE_BUFFERING宏可設(shè)置Buffer的個(gè)數(shù)。對某些內(nèi)存/顯存并不是很大的設(shè)備,也可以選擇不使用Triple Buffer。 2.2 Choreographer介紹Choreographer是一個(gè)Java類。第一次看到這個(gè)詞時(shí),我很激動。一個(gè)小小的命名真的反應(yīng)出了設(shè)計(jì)者除coding之外的廣博的視界。試想,如果不是對舞蹈有相當(dāng)了解或喜愛,一般人很難想到用這個(gè)詞來描述它。 Choreographer的定義和基本結(jié)構(gòu)如圖8所示:
圖8 Choreographer的定義和結(jié)構(gòu) 圖8中:
優(yōu)先級高低和處理順序有關(guān)。當(dāng)收到VSYNC中斷時(shí),Choreographer將首先處理INPUT類型的回調(diào),然后是ANIMATION類型,最后才是TRAVERSAL類型。 此外,讀者在自行閱讀Choreographer相關(guān)代碼時(shí),還會發(fā)現(xiàn)Android對Message/Looper類[③]也進(jìn)行了一番小改造,使之支持Asynchronous Message和Synchronization Barrier(參照Looper.java的postSyncBarrier函數(shù))。其實(shí)現(xiàn)非常巧妙,這部分內(nèi)容就留給讀者自己理解并欣賞了。 相比SurfaceFlinger,Choreographer是Android 4.1中的新事物,下面將通過一個(gè)實(shí)例來簡單介紹Choreographer的工作原理。 假如UI中有一個(gè)控件invalidate了,那么它將觸發(fā)ViewRootImpl的invalidate函數(shù),該函數(shù)將最終調(diào)用ViewRootImpl的scheduleTraversals。其代碼如圖9所示:
圖9 ViewRootImpl scheduleTraversals函數(shù)的實(shí)現(xiàn) 由圖9可知,scheduleTraversals首先禁止了后續(xù)的消息處理功能,這是由設(shè)置Looper的postSyncBarrier來完成的。一旦設(shè)置了SyncBarrier,所有非Asynchronous的消息便將停止派發(fā)。 然后,為Choreographer設(shè)置了CALLBACK類型為TRAVERSAL的處理對象,即mTraversalRunnable。 最后調(diào)用scheduleConsumeBatchedInput,這個(gè)函數(shù)將為Choreographer設(shè)置了CALLBACK類型為INPUT的處理對象。 Choreographer的postCallback函數(shù)將會申請一次VSYNC中斷(通過調(diào)用DisplayEventReceiver的scheduleVsync實(shí)現(xiàn))。當(dāng)VSYNC信號到達(dá)時(shí),Choreographer doFrame函數(shù)被調(diào)用,內(nèi)部代碼會觸發(fā)回調(diào)處理。代碼片段如圖10所示:
圖10 Choreographer doFrame函數(shù)片段 對ViewRootImpl來說,其TRAVERSAL回調(diào)對應(yīng)的處理對象,就是前面介紹的mTraversalRunnable,它的代碼很簡單,如圖11所示:
圖11 mTraversalRunnable的實(shí)現(xiàn) doTraversal內(nèi)部實(shí)現(xiàn)和Android 4.0版本一致。故相比于4.0來說,4.1只是把doTraversal調(diào)用位置放到VSYNC中斷處理中了。 通過上邊的介紹,可知Choreographer確實(shí)做到了對繪制工作的統(tǒng)一安排,不愧是個(gè)長于統(tǒng)籌安排的“舞蹈編導(dǎo)”。 三總結(jié)本文通過對Android Project Butter的分析,向讀者介紹了VSYNC原理以及Android Display系統(tǒng)的實(shí)現(xiàn)。除了VSYNC外,Project Butter還包括其他一些細(xì)節(jié)的改進(jìn),例如避免重疊區(qū)域的繪制等。 簡言之,Project Butter從本質(zhì)上解決了Android UI不流暢的問題,而且從Google I/O給出的視頻來看,其效果相當(dāng)不錯(cuò)。但實(shí)際上它對硬件配置還是有一定要求的。因?yàn)?span style="font-family:'Times New Roman'">VSYNC中斷處理的線程優(yōu)先級一定要高,否則EventThread接收到VSYNC中斷,卻不能及時(shí)去處理,那就喪失同步的意義了。所以,筆者估計(jì)目前將有一大批單核甚至雙核機(jī)器無法嘗到Jelly Bean了。 |
|