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中實現了同步機制。

  1. 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

  1. class LayerVector : public SortedVector< sp<LayerBase> > {  
  2. public:  
  3.     LayerVector() { }  
  4.     LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }  
  5.     virtual int do_compare(const void* lhs, const void* rhs) const {  
  6.         const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));  
  7.         const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));  
  8.         // sort layers by Z order  
  9.         uint32_t lz = l->currentState().z;  
  10.         uint32_t rz = r->currentState().z;  
  11.         // then by sequence, so we get a stable ordering  
  12.         return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);  
  13.     }  
  14. };  
    每次向layersSortedByZ中添加新的Layer,都會做一次排序,按照規則將其放在合適的位置。

    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
  1. // wallpaper is at the bottom, though the window manager may move it.  
  2.  static final int WALLPAPER_LAYER = 2;  
  3.  static final int APPLICATION_LAYER = 2;  
  4.  static final int PHONE_LAYER = 3;  
  5.  static final int SEARCH_BAR_LAYER = 4;  
  6.  static final int STATUS_BAR_PANEL_LAYER = 5;  
  7.  static final int SYSTEM_DIALOG_LAYER = 6;  
  8.  // toasts and the plugged-in battery thing  
  9.  static final int TOAST_LAYER = 7;  
  10.  static final int STATUS_BAR_LAYER = 8;  
  11.  // SIM errors and unlock.  Not sure if this really should be in a high layer.  
  12.  static final int PRIORITY_PHONE_LAYER = 9;  
  13.  // like the ANR / app crashed dialogs  
  14.  static final int SYSTEM_ALERT_LAYER = 10;  
  15.  // system-level error dialogs  
  16.  static final int SYSTEM_ERROR_LAYER = 11;  
  17.  // on-screen keyboards and other such input method user interfaces go here.  
  18.  static final int INPUT_METHOD_LAYER = 12;  
  19.  // on-screen keyboards and other such input method user interfaces go here.  
  20.  static final int INPUT_METHOD_DIALOG_LAYER = 13;  
  21.  // the keyguard; nothing on top of these can take focus, since they are  
  22.  // responsible for power management when displayed.  
  23.  static final int KEYGUARD_LAYER = 14;  
  24.  static final int KEYGUARD_DIALOG_LAYER = 15;  
  25.  // things in here CAN NOT take focus, but are shown on top of everything else.  
  26.  static final int SYSTEM_OVERLAY_LAYER = 16;  
  27.  static final int SECURE_SYSTEM_OVERLAY_LAYER = 17;  
    

    sequence值如何確定?

    sequence值是根據Layer的創建的順序來維護這個序列值,下面代碼中的LayerBase的構造函數中的sequence值,每創建一個Layer,sSequence加一賦值給sequence。

@LayerBase.cpp

  1. int32_t LayerBase::sSequence = 1;  
  2.   
  3. LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)  
  4.     : dpy(display), contentDirty(false),  
  5.       sequence(uint32_t(android_atomic_inc(&sSequence))),  
  6.       mFlinger(flinger),  
  7.       mNeedsFiltering(false),  
  8.       mOrientation(0),  
  9.       mLeft(0), mTop(0),  
  10.       mTransactionFlags(0),  
  11.       mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),  
  12.       mInvalidate(0)  
  13. {  
  14.     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());  
  15.     mFlags = hw.getFlags();  
  16.     mBufferCrop.makeInvalid();  
  17.     mBufferTransform = 0;  
  18. }  

3. 屬性更新

    這一節的所描述的實現都在函數handleTransactionLocked()中。

    從上面概括圖中可以看出,WMS可以對SurfaceFlinger進行屬性設置,也可以對當前的Surface對應的Layer進行屬性設置,因此handleTransactionLocked()函數就是對SurfaceFlinger屬性和設置了新屬性的Layer的屬性更新。

  1. enum {  
  2.     eTransactionNeeded      = 0x01,  
  3.     eTraversalNeeded        = 0x02  
  4. };  
    SurfaceFlinger根據這個枚舉值來確定handleTransactionLocked()需要更新SurfaceFlinger屬性還是layer屬性。

    如果SurfaceFlinger屬性被設置了新內容,則SurfaceFlinger會記錄標誌eTransactionNeeded;如果layer屬性被設置了新內容,那麼
SurfaceFlinger會記錄標誌eTraversalNeeded。handleTransactionLocked()通過記錄的標誌來執行各自的屬性得更新。‘

    這裏提到的屬性的更新,主要是看SurfaceFlinger或者laye新設置的屬性與舊的屬性相比,哪些屬性做了修改,然後

記錄下來,在接下來的SurfaceFlinger loop中使用新的屬性來顯示圖形。

    類SurfaceFlinger 和Layer中各自定義了兩個屬性的變量,其中mCurrentState爲新設置屬性,mDrawingState爲顯示圖形時用到的屬性,一般爲舊屬性。不過類SurfaceFlinger 和Layer分別定義了不同的State類。

  1. State                   mCurrentState;  
  2. 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

  1. ssize_t buf = lcblk->retireAndLock();  

SharedBufferServer::RetireUpdate::operator()@SharedBufferStack.cpp

  1. head = (head + 1) % numBuffers;  

    


4.2 紋理初始化

    爲每個Buffer的紋理進行初始化,爲當前的紋理創建一個EGLImageKHR,將當前的Buffer最爲該EGLImageKHR的源。這樣OpenGL就可以進行紋理映射。

lockPageFlip()@Layer.cpp

  1. /* a buffer was posted, so we need to call reloadTexture(), which 
  2.  * will update our internal data structures (eg: EGLImageKHR or 
  3.  * texture names). we need to do this even if mPostedDirtyRegion is 
  4.  * empty -- it's orthogonal to the fact that a new buffer was posted, 
  5.  * for instance, a degenerate case could be that the user did an empty 
  6.  * update but repainted the buffer with appropriate content (after a 
  7.  * resize for instance). 
  8.  */  
  9. reloadTexture( mPostedDirtyRegion );  

4.3 計算顯示區域

    通過layer的疊加,我們可以計算出總的顯示區域以及每個layer需要顯示的區域,它的實現在computeVisibleRegions()函數中。這個函數主要計算了layer疊加後的總的顯示區域,以及每個layer需要顯示的區域。整個的計算過程比較簡單,只是需要注意不透明區域的處理,computeVisibleRegions()需要計算出一個不透明區域,通過這個不透明區域驗證WMS提供給layer的區域是否正確。即下面代碼中的mWormholeRegion計算,mWormholeRegion爲屏幕區域減去不透明區域,正常情況mWormholeRegion應該爲空,即不透明區域範圍應該爲屏幕區域,如果不透明區域小雨屏幕區域,那麼說明當前的應用程序出現了設置的錯誤。今天有個網友就出現了這個問題。

handlePageFlip()

  1. const Region screenRegion(hw.bounds());  
  2. if (visibleRegions) {  
  3.     Region opaqueRegion;  
  4.     computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);  
  5.   
  6.     /* 
  7.      *  rebuild the visible layer list 
  8.      */  
  9.     mVisibleLayersSortedByZ.clear();  
  10.     const LayerVector& currentLayers(mDrawingState.layersSortedByZ);  
  11.     size_t count = currentLayers.size();  
  12.     mVisibleLayersSortedByZ.setCapacity(count);  
  13.     for (size_t i=0 ; i<count ; i++) {  
  14.         if (!currentLayers[i]->visibleRegionScreen.isEmpty())  
  15.             mVisibleLayersSortedByZ.add(currentLayers[i]);  
  16.     }  
  17.   
  18.     mWormholeRegion = screenRegion.subtract(opaqueRegion);  
  19.     mVisibleRegionsDirty = false;  
  20. }  

    在computeVisibleRegions()疊加計算總的顯示範圍,layer的計算順序從上到下的過程計算的,也就是先計算Z-Order值較大的,顯示在最上層的layer開始往下計算。這麼做的好處就是能夠很好的計算出不透明區域的範圍。

    在SurfaceFlinger的區域相互之間的操作處理如下:


    

    

    


    

4.4 圖形緩存

    前面選擇了FrontBuffer、初始化了紋理、計算了layer的顯示區域,那麼下一步就該將Buffer內容進行圖形處理並保存到OpenGL緩存中。

    調用每個layer的draw函數來進行這個操作。如下面代碼所示。具體的圖形處理過程很複雜,完全交給OpenGL去處理,這裏我們就不去關心了。我們只需要知道最終經過圖形處理的內容會被緩存到OpenGL的緩存區中。

  1. void SurfaceFlinger::composeSurfaces(const Region& dirty)  
  2. {  
  3.     if (UNLIKELY(!mWormholeRegion.isEmpty())) {  
  4.         // should never happen unless the window manager has a bug  
  5.         // draw something...  
  6.         drawWormhole();  
  7.     }  
  8.     const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);  
  9.     const size_t count = layers.size();  
  10.     for (size_t i=0 ; i<count ; ++i) {  
  11.         const sp<LayerBase>& layer(layers[i]);  
  12.         const Region clip(dirty.intersect(layer->visibleRegionScreen));  
  13.         if (!clip.isEmpty()) {  
  14.             layer->draw(clip);  
  15.         }  
  16.     }  
  17. }  
    從前面的顯示系統中,介紹過,Surface的緩存Buffer就是FramebufferNativeWindow中定義的2個Buffer,如果/dev/fb0讀取設備信息,如果設備支持page flip,那麼Surface的緩存Buffer即從/dev/fb0設備中申請;如果不支持,我們則需要從/dev/pmem中申請,同時/dev/fb0還會提供一個Buffer以便圖形最終的顯示。

    /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

  1. /* 
  2.  * Handle eglSetSwapRectangleANDROID() 
  3.  * We copyback from the front buffer  
  4.  */  
  5. if (!dirtyRegion.isEmpty()) {  
  6.     dirtyRegion.andSelf(Rect(buffer->width, buffer->height));  
  7.     if (previousBuffer) {  
  8.         const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));  
  9.         if (!copyBack.isEmpty()) {  
  10.             void* prevBits;  
  11.             if (lock(previousBuffer,   
  12.                     GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {  
  13.                 // copy from previousBuffer to buffer  
  14.                 copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);  
  15.                 unlock(previousBuffer);  
  16.             }  
  17.         }  
  18.     }  
  19.     oldDirtyRegion = dirtyRegion;  
  20. }  
    其次,如果當前的緩存buffer是申請自/dev/fb0,那麼直接去顯示這個緩存區中內容;如果緩存buffer是申請自/dev/pmem,那麼需要將緩存buffer中內容拷貝到/dev/fb0 buffer中去,其結構如上一節所示。

    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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章