android surfaceflinger研究----SurfaceFlinger loop

来源:互联网 发布:数据魔方有什么用 编辑:程序博客网 时间:2024/04/29 14:01

转自 :http://blog.csdn.net/windskier/article/details/7060995


   SurfaceFlinger 是一个thread,运行在system_server进程中,并且其threadLoop()方法的返回值为true,因此它是一个循环的loop。这样保证了SurfaceFlinger业务的循环周期性。

    首先,先来个综述,下图是我总结的一个SurfaceFlinger结构的概括图:


1. SurfaceFlinger的同步

    SurfaceFlinger 并不是时时刻刻都在执行业务中,当WMS请求SurfaceFlinger创建Surface,或者WMS对Surface进行属性设置时,我们希望此时的SurfaceFlinger并不进行显示操作,以保证对Surface的线程保护,因此SurfaceFlinger 的loop中实现了同步机制。

view plain
  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

view plain
  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

view plain
  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

view plain
  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的属性更新。

view plain
  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类。

view plain
  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

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

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

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

    


4.2 纹理初始化

    为每个Buffer的纹理进行初始化,为当前的纹理创建一个EGLImageKHR,将当前的Buffer最为该EGLImageKHR的源。这样OpenGL就可以进行纹理映射。

lockPageFlip()@Layer.cpp

view plain
  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()

view plain
  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的缓存区中。

view plain
  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

view plain
  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)操作。


 


原创粉丝点击