上一篇文章介紹了整個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中實現了同步機制。
- waitForEvent();
主要的同步情況有如下幾種,當然也有其他一些要求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
- class LayerVector : public SortedVector< sp<LayerBase> > {
- public:
- LayerVector() { }
- LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
- virtual int do_compare(const void* lhs, const void* rhs) const {
- const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
- const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
- // sort layers by Z order
- uint32_t lz = l->currentState().z;
- uint32_t rz = r->currentState().z;
- // then by sequence, so we get a stable ordering
- return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
- }
- };
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
- // wallpaper is at the bottom, though the window manager may move it.
- static final int WALLPAPER_LAYER = 2;
- static final int APPLICATION_LAYER = 2;
- static final int PHONE_LAYER = 3;
- static final int SEARCH_BAR_LAYER = 4;
- static final int STATUS_BAR_PANEL_LAYER = 5;
- static final int SYSTEM_DIALOG_LAYER = 6;
- // toasts and the plugged-in battery thing
- static final int TOAST_LAYER = 7;
- static final int STATUS_BAR_LAYER = 8;
- // SIM errors and unlock. Not sure if this really should be in a high layer.
- static final int PRIORITY_PHONE_LAYER = 9;
- // like the ANR / app crashed dialogs
- static final int SYSTEM_ALERT_LAYER = 10;
- // system-level error dialogs
- static final int SYSTEM_ERROR_LAYER = 11;
- // on-screen keyboards and other such input method user interfaces go here.
- static final int INPUT_METHOD_LAYER = 12;
- // on-screen keyboards and other such input method user interfaces go here.
- static final int INPUT_METHOD_DIALOG_LAYER = 13;
- // the keyguard; nothing on top of these can take focus, since they are
- // responsible for power management when displayed.
- static final int KEYGUARD_LAYER = 14;
- static final int KEYGUARD_DIALOG_LAYER = 15;
- // things in here CAN NOT take focus, but are shown on top of everything else.
- static final int SYSTEM_OVERLAY_LAYER = 16;
- static final int SECURE_SYSTEM_OVERLAY_LAYER = 17;
sequence值如何確定?
sequence值是根據Layer的創建的順序來維護這個序列值,下面代碼中的LayerBase的構造函數中的sequence值,每創建一個Layer,sSequence加一賦值給sequence。
@LayerBase.cpp
- int32_t LayerBase::sSequence = 1;
- LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
- : dpy(display), contentDirty(false),
- sequence(uint32_t(android_atomic_inc(&sSequence))),
- mFlinger(flinger),
- mNeedsFiltering(false),
- mOrientation(0),
- mLeft(0), mTop(0),
- mTransactionFlags(0),
- mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
- mInvalidate(0)
- {
- const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
- mFlags = hw.getFlags();
- mBufferCrop.makeInvalid();
- mBufferTransform = 0;
- }
3. 屬性更新
這一節的所描述的實現都在函數handleTransactionLocked()中。
從上面概括圖中可以看出,WMS可以對SurfaceFlinger進行屬性設置,也可以對當前的Surface對應的Layer進行屬性設置,因此handleTransactionLocked()函數就是對SurfaceFlinger屬性和設置了新屬性的Layer的屬性更新。
- enum {
- eTransactionNeeded = 0x01,
- eTraversalNeeded = 0x02
- };
如果SurfaceFlinger屬性被設置了新內容,則SurfaceFlinger會記錄標誌eTransactionNeeded;如果layer屬性被設置了新內容,那麼
SurfaceFlinger會記錄標誌eTraversalNeeded。handleTransactionLocked()通過記錄的標誌來執行各自的屬性得更新。‘
這裏提到的屬性的更新,主要是看SurfaceFlinger或者laye新設置的屬性與舊的屬性相比,哪些屬性做了修改,然後
記錄下來,在接下來的SurfaceFlinger loop中使用新的屬性來顯示圖形。
類SurfaceFlinger 和Layer中各自定義了兩個屬性的變量,其中mCurrentState爲新設置屬性,mDrawingState爲顯示圖形時用到的屬性,一般爲舊屬性。不過類SurfaceFlinger 和Layer分別定義了不同的State類。
- State mCurrentState;
- State mDrawingState;
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
- ssize_t buf = lcblk->retireAndLock();
SharedBufferServer::RetireUpdate::operator()@SharedBufferStack.cpp
- head = (head + 1) % numBuffers;
4.2 紋理初始化
爲每個Buffer的紋理進行初始化,爲當前的紋理創建一個EGLImageKHR,將當前的Buffer最爲該EGLImageKHR的源。這樣OpenGL就可以進行紋理映射。
lockPageFlip()@Layer.cpp
- /* a buffer was posted, so we need to call reloadTexture(), which
- * will update our internal data structures (eg: EGLImageKHR or
- * texture names). we need to do this even if mPostedDirtyRegion is
- * empty -- it's orthogonal to the fact that a new buffer was posted,
- * for instance, a degenerate case could be that the user did an empty
- * update but repainted the buffer with appropriate content (after a
- * resize for instance).
- */
- reloadTexture( mPostedDirtyRegion );
4.3 計算顯示區域
通過layer的疊加,我們可以計算出總的顯示區域以及每個layer需要顯示的區域,它的實現在computeVisibleRegions()函數中。這個函數主要計算了layer疊加後的總的顯示區域,以及每個layer需要顯示的區域。整個的計算過程比較簡單,只是需要注意不透明區域的處理,computeVisibleRegions()需要計算出一個不透明區域,通過這個不透明區域驗證WMS提供給layer的區域是否正確。即下面代碼中的mWormholeRegion計算,mWormholeRegion爲屏幕區域減去不透明區域,正常情況mWormholeRegion應該爲空,即不透明區域範圍應該爲屏幕區域,如果不透明區域小雨屏幕區域,那麼說明當前的應用程序出現了設置的錯誤。今天有個網友就出現了這個問題。
handlePageFlip()
- const Region screenRegion(hw.bounds());
- if (visibleRegions) {
- Region opaqueRegion;
- computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
- /*
- * rebuild the visible layer list
- */
- mVisibleLayersSortedByZ.clear();
- const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
- size_t count = currentLayers.size();
- mVisibleLayersSortedByZ.setCapacity(count);
- for (size_t i=0 ; i<count ; i++) {
- if (!currentLayers[i]->visibleRegionScreen.isEmpty())
- mVisibleLayersSortedByZ.add(currentLayers[i]);
- }
- mWormholeRegion = screenRegion.subtract(opaqueRegion);
- mVisibleRegionsDirty = false;
- }
在computeVisibleRegions()疊加計算總的顯示範圍,layer的計算順序從上到下的過程計算的,也就是先計算Z-Order值較大的,顯示在最上層的layer開始往下計算。這麼做的好處就是能夠很好的計算出不透明區域的範圍。
在SurfaceFlinger的區域相互之間的操作處理如下:
4.4 圖形緩存
前面選擇了FrontBuffer、初始化了紋理、計算了layer的顯示區域,那麼下一步就該將Buffer內容進行圖形處理並保存到OpenGL緩存中。
調用每個layer的draw函數來進行這個操作。如下面代碼所示。具體的圖形處理過程很複雜,完全交給OpenGL去處理,這裏我們就不去關心了。我們只需要知道最終經過圖形處理的內容會被緩存到OpenGL的緩存區中。
- void SurfaceFlinger::composeSurfaces(const Region& dirty)
- {
- if (UNLIKELY(!mWormholeRegion.isEmpty())) {
- // should never happen unless the window manager has a bug
- // draw something...
- drawWormhole();
- }
- const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<LayerBase>& layer(layers[i]);
- const Region clip(dirty.intersect(layer->visibleRegionScreen));
- if (!clip.isEmpty()) {
- layer->draw(clip);
- }
- }
- }
/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
- /*
- * Handle eglSetSwapRectangleANDROID()
- * We copyback from the front buffer
- */
- if (!dirtyRegion.isEmpty()) {
- dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
- if (previousBuffer) {
- const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
- if (!copyBack.isEmpty()) {
- void* prevBits;
- if (lock(previousBuffer,
- GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
- // copy from previousBuffer to buffer
- copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
- unlock(previousBuffer);
- }
- }
- }
- oldDirtyRegion = dirtyRegion;
- }
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