android surfaceflinger研究----SurfaceFlinger loop
?? 上一篇文章介紹了整個Surface機制(我是這么稱呼的,主要是Surface的建立,Surface的顯示存儲的管理),同時我們也介紹過了整個顯示系統,那么這篇文章就介紹一下SurfaceFlinger 這個核心服務層的機制。
? ? 從代碼中我們可以看出SurfaceFlinger 是一個thread,運行在system_server進程中,并且其threadLoop()方法的返回值為true,因此它是一個循環的loop。這樣保證了SurfaceFlinger業務的循環周期性。
? ? 首先,先來個綜述,下圖是我總結的一個SurfaceFlinger結構的概括圖:
1. SurfaceFlinger的同步
? ??SurfaceFlinger 并不是時時刻刻都在執行業務中,當WMS請求SurfaceFlinger創建Surface,或者WMS對Surface進行屬性設置時,我們希望此時的SurfaceFlinger并不進行顯示操作,以保證對Surface的線程保護,因此SurfaceFlinger 的loop中實現了同步機制。
[cpp]?view plaincopy
??? 主要的同步情況有如下幾種,當然也有其他一些要求SurfaceFlinger同步的情況,不夠對于研究SurfaceFlinger就不太重要了
? ? 1. 創建Surface同步
? ? 假如當前只有一個Client,比如WMS請求SufaceFlinger創建一個Surface,那么此時應該保持SurfaceFlinger loop處在block狀態,因為這個過程涉及到對一些成員變量的處理,為了保證同步而需要hold住整個loop。
? ? 2. 設置Surface屬性或SurfaceFlinger屬性同步
? ? 創建完Surface之后,WMS會請求SurfaceFlinger對其Layer進行屬性設置或者對SurfaceFlinger的屬性進行設置,如上面概括圖中SurfaceComposerClient中的函數接口。
? ? 3. Surface繪制同步
? ? 當ViewRoot對Surface進行繪制時,同樣需要將SurfaceFlinger hold住,當整個窗口繪制完成之后,再向SurfaceFlinger發送signal信號。如下面時序圖所示。
? ??
? ? 4. freeze/unfreeze同步
? ?當每個Activity啟動的時候,AMS都會請求WMS freeze整個屏幕,當Activity啟動之后,再unfreeze整個屏幕,我猜測這么做的目的是為了保證在Activity以及Activity的窗口在創建過程中,對Activity窗口的Surface進行的線程保護,以免出現屏幕的閃爍等用戶體驗較差的現象。
2. Layer存儲
? ? 在SurfaceFlinger中,Layer是怎么樣存儲的呢?所有的Layer,不論是那個Client創建的Layer,均保存在一個名為layersSortedByZ的變量中,也就是說WMS請求創建的Surface的Layer和其他Client請求創建的Layer都保存在layersSortedByZ中,但是layersSortedByZ保存過程中則遵守一定的規則。下面代碼中的do_compare揭示了這個規則。
@SurfaceFlinger.h
[cpp]?view plaincopy
? ? 1. 首先,按照Layer的Z-order值來排序,Z-order值小的,放在layersSortedByZ低索引值位置;
? ? 2. 其次,如果兩個Layer?Z-order值相同,sequence值小的,放在layersSortedByZ低索引值位置;
? ??Z-order值如何確定?
? ? WMS根據不同的Window Type來確定Z-order值,Z-order = LAYER*TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET。
根據下面代碼中的不同的Window Type的LAYER值,可以確定Z-order值,例如TYPE_APPLICATION窗口,其
Z-order = 2*10000+1000 = 21000。
? ? @PhoneWindowManager.java
[java]?view plaincopy? ??sequence值如何確定?
? ??sequence值是根據Layer的創建的順序來維護這個序列值,下面代碼中的LayerBase的構造函數中的sequence值,每創建一個Layer,sSequence加一賦值給sequence。
@LayerBase.cpp
[cpp]?view plaincopy
3.?屬性更新
? ? 這一節的所描述的實現都在函數handleTransactionLocked()中。
? ? 從上面概括圖中可以看出,WMS可以對SurfaceFlinger進行屬性設置,也可以對當前的Surface對應的Layer進行屬性設置,因此handleTransactionLocked()函數就是對SurfaceFlinger屬性和設置了新屬性的Layer的屬性更新。
[cpp]?view plaincopy
? ? 如果SurfaceFlinger屬性被設置了新內容,則SurfaceFlinger會記錄標志eTransactionNeeded;如果layer屬性被設置了新內容,那么
SurfaceFlinger會記錄標志eTraversalNeeded。handleTransactionLocked()通過記錄的標志來執行各自的屬性得更新。‘
? ? 這里提到的屬性的更新,主要是看SurfaceFlinger或者laye新設置的屬性與舊的屬性相比,哪些屬性做了修改,然后
記錄下來,在接下來的SurfaceFlinger loop中使用新的屬性來顯示圖形。
? ? 類SurfaceFlinger 和Layer中各自定義了兩個屬性的變量,其中mCurrentState為新設置屬性,mDrawingState為顯示圖形時用到的屬性,一般為舊屬性。不過類SurfaceFlinger 和Layer分別定義了不同的State類。
[cpp]?view plaincopy
4. 圖形緩存
? ? 這一部分的的實現在函數handlePageFlip()中。
? ? 有這么一種可能,當前顯示到顯示設備上的layer不止一個,而且layer是按照Z-Order的順序來疊加到OpenGL的surface上的,那么這就需要layer的Z-Order值和坐標來確定每個layer能夠被顯示的區域。
4.1 page flip
? ? 前面一篇文章中介紹過,每個surface均有2個buffer供使用,一個作為FronteBuffer供SurfaceFlinger去顯示,另外一個作為BackBuffer供ViewRoot去繪制窗口。因此在顯示各個layer之前,我們需要做一個page flip過程,將當前的已經繪制了應用窗口的BackBuffer選擇為FrontBuffer,用于顯示;將之前的已經顯示完成的FrontBuffer在重置為BackBuffer供ViewRoot去繪制。
? ?而實現這個page flip的過程很簡單
lockPageFlip()@Layer.cpp
[cpp]?view plaincopy
SharedBufferServer::RetireUpdate::operator()@SharedBufferStack.cpp
? ??
4.2 紋理初始化
? ? 為每個Buffer的紋理進行初始化,為當前的紋理創建一個EGLImageKHR,將當前的Buffer最為該EGLImageKHR的源。這樣OpenGL就可以進行紋理映射。
lockPageFlip()@Layer.cpp
[cpp]?view plaincopy
4.3 計算顯示區域
? ? 通過layer的疊加,我們可以計算出總的顯示區域以及每個layer需要顯示的區域,它的實現在computeVisibleRegions()函數中。這個函數主要計算了layer疊加后的總的顯示區域,以及每個layer需要顯示的區域。整個的計算過程比較簡單,只是需要注意不透明區域的處理,computeVisibleRegions()需要計算出一個不透明區域,通過這個不透明區域驗證WMS提供給layer的區域是否正確。即下面代碼中的mWormholeRegion計算,mWormholeRegion為屏幕區域減去不透明區域,正常情況mWormholeRegion應該為空,即不透明區域范圍應該為屏幕區域,如果不透明區域小雨屏幕區域,那么說明當前的應用程序出現了設置的錯誤。今天有個網友就出現了這個問題。
handlePageFlip()
[cpp]?view plaincopy
? ? 在computeVisibleRegions()疊加計算總的顯示范圍,layer的計算順序從上到下的過程計算的,也就是先計算Z-Order值較大的,顯示在最上層的layer開始往下計算。這么做的好處就是能夠很好的計算出不透明區域的范圍。
? ? 在SurfaceFlinger的區域相互之間的操作處理如下:
? ??
? ??
? ??
? ??
4.4 圖形緩存
? ? 前面選擇了FrontBuffer、初始化了紋理、計算了layer的顯示區域,那么下一步就該將Buffer內容進行圖形處理并保存到OpenGL緩存中。
? ? 調用每個layer的draw函數來進行這個操作。如下面代碼所示。具體的圖形處理過程很復雜,完全交給OpenGL去處理,這里我們就不去關心了。我們只需要知道最終經過圖形處理的內容會被緩存到OpenGL的緩存區中。
[cpp]?view plaincopy
? ??/dev/fb0不支持page flip模式
? ??
? ??/dev/fb0支持page flip模式
? ??
5. 圖形顯示
? ? 當圖形內容被緩存到frameBuffer中后,最后的一步就是圖形顯示。代碼中很明確就是SurfaceFlinger loop中的postFramebuffer()函數了。
? ? 這個函數最終回調到OpenGL的eglSwapBuffers()函數,這個函數主要有2個步驟(由于硬件加速代碼不可見,我們仍然以軟件加速為例)
? ? 1. 顯示當前緩存buffer中內容;
? ? ?首先,將原來的屏幕上的內容與最新需要顯示的內容進行區域相減,將原來的內容copy到當前的緩存buffer中;
EGLBoolean egl_window_surface_v2_t::swapBuffers()@frameworks\base\opengl\libagl\egl.cpp
[cpp]?view plaincopy
? ? 2. 對2個緩存buffer進行page flip(swap)操作。
? ? 通過?queueBuffer()操作將將當前Buffer交還給FramebufferNativeWindow,同時調用fb_post進行圖形顯示。然后通過dequeueBuffer()操作獲得另外一個FramebufferNativeWindow的緩存Buffer,實現page flip(swap)操作。
?
? ? 至此,整個的SurfaceFlinger的機制就分析完了。
原文地址:?http://blog.csdn.net/windskier/article/details/7060995
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的android surfaceflinger研究----SurfaceFlinger loop的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android surfacefling
- 下一篇: Android 5.0及以上实现屏幕截图