Android系统Surface机制的SurfaceFlinger服务渲染应用程序UI的过程分析(1)

简介:

  在前面的一系列文章中,我们学习了Android应用程序与SurfaceFlinger服务的关系,以及SurfaceFlinger服务的启动过程、初始化硬件帧缓冲区的过程、线程模型。SurfaceFlinger服务所做的一切都是为了给Android应用程序提服务的,即为Android应用程序渲染它们的UI。在本文中,我们就详细分析SurfaceFlinger服务渲染Android应用程序UI的过程。

       从前面Android系统Surface制的SurfaceFlinger服务的线程模型分析一文可以知道,SurfaceFlinger服务是通过它的UI渲染线程来将应用程序的UI渲染到硬件帧缓冲区中去的,因此,接下来我们就通过分析SurfaceFlinger服务的UI渲染线程的执行过程来分应用程序UI的渲染过程,这个过程如图1所示。

1350489913_5651.jpg

图1 SurfaceFlinger服务渲染应用程序UI的示意图

       从图1就可以看出,SurfaceFlinger服务的UI渲染线程的执行过程如下所示:

       1. 调用SurfaceFlinger类的成员函数handleConsoleEvents来处理控制台事件。

       2. 调用SurfaceFlinger类的成员函数handleTransaction来处理系统显示屏以及应用程序窗口的属性变化,例如大小、旋转方向变化等。

       3. 调用SurfaceFlinger类的成员函数handlePageFlip来让各个应用程序窗口设置它们当前所要渲染的图形缓冲区。

       4. 如果SurfaceFlinger服务在编译的时候指定了USE_COMPOSITION_BYPASS宏,并且当前需要渲染的应用程序窗口只有一个,那么就会调用SurfaceFlinger类的成员函数handleBypassLayer来直接将这个应用程序窗口的图形缓冲区渲染到硬件帧缓冲区中去,否则的话,就要调用SurfaceFlinger类的成员函数handleRepaint来合成所有的应用程序窗口的图形缓冲区到一个主图形缓冲区中去。

       5. 调用SurfaceFlinger类的成员函数postFramebuffer将前面得到的主图形缓冲区渲染到硬件帧缓冲区中去。

       前面Android系统Surface制的SurfaceFlinger服务的线程模型分析一文中,我们已经分析过第1步的实现了,而通过前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划这一系列文章的学习,我们也已经了解了应用程序窗口的图形缓冲区的创建过程,因此,接下来我们就在这些知识的基础上来详细分析第2步到第5的实现,即分别分析SurfaceFlinger类的成员函数handleTransaction、handlePageFlip、handleBypassLayer和postFramebuffer的实现。

      1. handleTransaction

      SurfaceFlinger类的成员函数handleTransaction是用来处理系统显示屏以及应用程序窗口的属性变化的,这个过程如图2所示。

1350491987_1022.jpg

图2 系统显示屏以及应用程序窗口的属性变化处理过程

        这个过程可以分为6个步骤,接下来我们就详细分析每一个步骤。

        Step 1. SurfaceFlinger.handleTransaction

1
2
3
void  SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
     Vector< sp<layerbase> > ditchedLayers;     /*     * Perform and commit the transaction     */    { // scope for the lock        Mutex::Autolock _l(mStateLock);        const nsecs_t now = systemTime();        mDebugInTransaction = now;        handleTransactionLocked(transactionFlags, ditchedLayers);        mLastTransactionTime = systemTime() - now;        mDebugInTransaction = 0;        // here the transaction has been committed    }    /*     * Clean-up all layers that went away     * (do this without the lock held)     */     const  size_t count = ditchedLayers.size();     for  (size_t i= 0  ; i<count>ditch();        }    }}</count></layerbase>

       这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。


       SurfaceFlinger类的成员函数handleTransaction是通过调用另外一个成员函数handleTransactionLocked来处理系统显示屏以及应用程序窗口的属性变化的,而SurfaceFlinger类的成员函数handleTransactionLocked在处理完成系统显示屏以及应用程序窗口的属性变化之后,会返回系统中那些已经销毁了的应用程序窗口。

       从Android应用程序与SurfaceFlinger服务的关系概述和学习计划这一系列文章可以知道,在SurfaceFlinger服务这一侧,应用程序窗口一般是使用一个Layer对象来描述的,又由于Layer类是从LayerBase类继承下来的,因此,我们可以那些已经销毁了的应用程序窗口保存在一个类型为sp的向量ditchedLayers中。

       SurfaceFlinger类的成员函数handleTransaction最后就调用保存在向量ditchedLayers中的每一个LayerBase对象的成员函数dtich来执行被销毁的应用程序窗口的清理操作,接下来我们就继续分析SurfaceFlinger类的成员函数handleTransactionLocked,看看它是如何处理系统显示屏以及应用程序窗口的属性变化的。

       Step 2. SurfaceFlinger.handleTransactionLocked

       SurfaceFlinger类的成员函数handleTransactionLocked定义在文件rameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,我们分三段来阅读:

1
2
void  SurfaceFlinger::handleTransactionLocked(
         uint32_t transactionFlags, Vector< sp<layerbase> >& ditchedLayers){     const  LayerVector& currentLayers(mCurrentState.layersSortedByZ);     const  size_t count = currentLayers.size();     /*     * Traversal of the children     * (perform the transaction for each of them if needed)     */     const  bool layersNeedTransaction = transactionFlags & eTraversalNeeded;     if  (layersNeedTransaction) {         for  (size_t i= 0  ; i<count ; i++) {             const  sp<layerbase>& layer = currentLayers[i];            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);             if  (!trFlags)  continue ;             const  uint32_t flags = layer->doTransaction( 0 );             if  (flags & Layer::eVisibleRegion)                mVisibleRegionsDirty =  true ;        }    }</layerbase></layerbase>

       这段代码用来处理应用程序窗口的属性变化。


       参数transactionFlags最开始是从SurfaceFlinger类的成员函数threadLoop传进来的。从前面Android系统Surface制的SurfaceFlinger服务的线程模型分析一文可以知道,SurfaceFlinger类的成员函数threadLoop在调用另外一个成员函数handleTransaction来处理系统显示屏以及应用程序窗口的属性变化之前,首先会调用成员函数getTransactionFlags来检查系统显示屏或者应用程序窗口的属性是否发生了变化。如果系统显示屏的属性发生了变化,那么传到这里的参数transactionFlags的eTransactionNeeded位就会等于1,而如果有应用程序窗口的属性发生了变化,那么传到这里的参数transactionFlags的eTraversalNeeded位就会等于1。为了方便描述,我们假设系统显示屏以及应用程序窗口的属性都发生了变化。

      SurfaceFlinger类的成员变量mCurrentState指向了一个State对象,用来描述SufaceFlinger服务的当前状态,其中,这个State对象的成员变量layersSortedByZ是一个类型为LayerVector的向量,它里面保存了SufaceFlinger服务当前所需要渲染的应用程序窗口,而这些应用程序窗口都是使用一个LayerBase对象来描述的。

      这段代码首先获得SufaceFlinger服务当前所需要渲染的应用程序窗口,接着再通过一个for循环来依次检查每一个应用程序窗口的属性是否发生了变化。如果某一个应用程序窗口的属性被修改过,那么调用用来描述这个应用程序窗口的一个LayerBase对象的成员函数getTransactionFlags得到的返回值trFlags就不会等于0,在这种情况下,这段代码就会调用这个LayerBase对象的成员函数doTransaction来处理对应的应用程序窗口的属性变化。

      在LayerBase类中,有一个类型为int32_t的成员变量mTransactionFlags,每当SurfaceFlinger服务修改某一个应用程序窗口的属性时,都会将与其对应的LayerBase的成员变量mTransactionFlags的相应的位设置为1,这样LayerBase类的成员函数getTransactionFlags就可以通过这个成员变量来判断一个应用程序窗口的属性是否发生变化了。

      如果一个应用程序窗口发生的属性变化是可见区域发生了改变,那么对应的LayerBase对象的成员函数doTransaction的返回值flags的Layer::eVisibleRegion位就会等于1。在这种情况下,这段代码就会将 SurfaceFlinger类的成员变量mVisibleRegionsDirty的值设置为true,表示后面要重新计算各个应用程序窗口的可见区域。

     为了方便描述,我们假设发生了属性变化的应用程序窗口是一个普通类型的Surface,即用来描述它的实际是一个从LayerBase类继承下来的Layer对象。在这种情况下,前面实际上调用了Layer类的成员函数doTransaction来处理一个应用程序窗口的属性变化。在接下来的Step 3中,我们再详细分析Layer类的成员函数doTransaction的实现,现在我们接着往下阅读SurfaceFlinger类的成员函数handleTransactionLocked的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
  * Perform our own transaction if needed
  */
if  (transactionFlags & eTransactionNeeded) {
     if  (mCurrentState.orientation != mDrawingState.orientation) {
         // the orientation has changed, recompute all visible regions
         // and invalidate everything.
         const  int  dpy =  0 ;
         const  int  orientation = mCurrentState.orientation;
         const  uint32_t type = mCurrentState.orientationType;
         GraphicPlane& plane(graphicPlane(dpy));
         plane.setOrientation(orientation);
         // update the shared control block
         const  DisplayHardware& hw(plane.displayHardware());
         volatile  display_cblk_t* dcblk = mServerCblk->displays + dpy;
         dcblk->orientation = orientation;
         dcblk->w = plane.getWidth();
         dcblk->h = plane.getHeight();
         mVisibleRegionsDirty =  true ;
         mDirtyRegion.set(hw.bounds());
     }
     if  (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) {
         // freezing or unfreezing the display -> trigger animation if needed
         mFreezeDisplay = mCurrentState.freezeDisplay;
         if  (mFreezeDisplay)
              mFreezeDisplayTime =  0 ;
     }
     if  (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
         // layers have been added
         mVisibleRegionsDirty =  true ;
     }
     // some layers might have been removed, so
     // we need to update the regions they\'re exposing.
     if  (mLayersRemoved) {
         mLayersRemoved =  false ;
         mVisibleRegionsDirty =  true ;
         const  LayerVector& previousLayers(mDrawingState.layersSortedByZ);
         const  size_t count = previousLayers.size();
         for  (size_t i= 0  ; i<count ;= ""  i++)= ""  {= ""  const = ""  sp<layerbase= "" >& layer(previousLayers[i]);             if  (currentLayers.indexOf( layer ) <  0 ) {                 // this layer is not visible anymore                ditchedLayers.add(layer);                mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);            }        }    }}</count>

       这段代码用来处理系统显示屏的属性变化。

       在分析这段代码之前,我们首先了解SurfaceFlinger类的另外一个成员变量mDrawingState的含义。SurfaceFlinger类的成员变量mDrawingState与前面所介绍的成员变量mCurrentState类似,它的类型也为State,不过它是用来描述SufaceFlinger服务的上一次渲染状态的。通过这两个成员变量的比较,我们就可以知道系统显示屏的哪一个属性发生了变化。

       前面提到,当系统显示屏的属性发生了变化,那么参数transactionFlags的eTransactionNeeded位就会等于1,在这种情况,这段代码就需要完成四件事情。

       第一件事情是判断系统显示屏的旋转方向是否发生变化。State类的成员变量orientation用来描述显示屏的方向,因此,当SurfaceFlinger类的成员变量mCurrentState所描述的一个State对象的成员变量orientation的值不等于SurfaceFlinger类的成员变量mDrawingState所描述的一个State对象的成员变量orientation的值时,就说明系统显示屏的旋转方向发生了变化。在这种情况下,我们就需要将系统显示屏的旋转方向设置为SurfaceFlinger类的成员变量mCurrentState所描述的一个State对象的成员变量orientation的值,这是通过调用编号为0的一个GraphicPlane对象的成员函数setOrientation来实现的。

      SurfaceFlinger服务的UI渲染线程在初始化的过程中,除了会初始化硬件帧缓冲区之外,还会创建一个类型为surface_flinger_cblk_t的对象,用来描述系统显示屏的信息,例如大小和旋转方向等,以便其它进程可以通过这个surface_flinger_cblk_t对象来获得系统显示屏的信息。这个surface_flinger_cblk_t对象就保存在SurfaceFlinger类的成员变量mServerCblk中。因此,当系统显示屏的旋转方向发生了变化时,我们还需要将变化后的旋转方向保存在SurfaceFlinger类的成员变量mServerCblk所描述的一个surface_flinger_cblk_t对象中。由于系统显示屏的旋转方向变化一般意味着宽度和高度也会发生变化,因此,我们还需要将旋转发生变化后得到的系统显示屏的宽度和高度值保存在SurfaceFlinger类的成员变量mServerCblk所描述的一个surface_flinger_cblk_t对象中。

      系统显示屏的旋转方向同时也意味着我们需要重新计算各个应用程序窗口的可见区域以及重新绘制整个显示屏,因此,在这种情况下,我们还需要将SurfaceFlinger类的成员变量mVisibleRegionsDirty的值设置为true,以及将SurfaceFlinger类的成员变量mDirtyRegion的大小设置为整个显示屏的大小,即将系统UI的脏区域设置为整个显示屏的大小。

      第二件事情是判断系统显示屏的冻结状态是否发生变化。State类的成员变量freezeDisplay用来描述显示屏的冻结状态,因此,当SurfaceFlinger类的成员变量mCurrentState所描述的一个State对象的成员变量freezeDisplay的值不等于SurfaceFlinger类的成员变量mDrawingState所描述的一个State对象的成员变量freezeDisplay的值时,就说明系统显示屏的冻结状态发生了变化。在这种情况下,我们就需要将SurfaceFlinger类的成员变量mFreezeDisplay的值设置为SurfaceFlinger类的成员变量mCurrentState所描述的一个State对象的成员变量freezeDisplay的值。如果显示屏的是由解冻状态变化为冻结状态的,那么还需要将显示屏的冻结时间设置为0,即将SurfaceFlinger类的成员变量mFreezeDisplayTime的值设置为0,以便可以将显示屏进入到冻结状态的最长时间设置为一个默认值,这一点可以参考前面Android系统Surface制的SurfaceFlinger服务的线程模型分析一文。

      第三件事情是判断是否新增了应用程序窗口。State类的成员变量layersSortedByZ是一个类型LayerVector的向量,里面保存的是SurfaceFlinger服务在某一个状态下所拥有的应用程序窗口,因此,当SurfaceFlinger类的成员变量mCurrentState所描述的一个State对象的成员变量layersSortedByZ所指向的一个向量的大小值大于SurfaceFlinger类的成员变量mDrawingState所描述的一个State对象的成员变量layersSortedByZ所指向的一个向量的大小值时,就说明系统新增了应用程序窗口。在这种情况下,我们就需要将SurfaceFlinger类的成员变量mVisibleRegionsDirty的值设置为true,以表示我们需要重新计算各个应用程序窗口的可见区域。

      第四件事情是判断是否移除了某些应用程序窗口。SurfaceFlinger类的成员变量mLayersRemoved用来描述是否有应用程序窗口被移除了。如果有有应用程序窗口被移除的话,那么这个成员变量的值就会等于true。在这种情况下,我们就需要是哪些应用程序窗口被移除了。计算的方法很简单,如果一个应用程序窗口存在于SurfaceFlinger类的成员变量mDrawingState所描述的一个State对象的成员变量layersSortedByZ所指向的一个向量中,但是不存在于SurfaceFlinger类的成员变量mCurrentState所描述的一个State对象的成员变量layersSortedByZ所指向的一个向量中,那么就说明这个应用程序窗口被移除了,因此,就需要将它保存输出参数ditchedLayers所描述的一个向量中,以便可以返回给上一步来处理。SurfaceFlinger类的成员变量mDirtyRegionRemovedLayer用来描述那些被移除了的应用程序窗口所占用的区域,因此,每当我们移除一个应用程序窗口的时候,都需要将它所占用的区域增加到SurfaceFlinger类的成员变量mDirtyRegionRemovedLayer所描述的一个区域去。

     当处理完成那些被移除的应用程序窗口之后,我们就需要将SurfaceFlinger类的成员变量mLayersRemoved的值设置为false,并且将SurfaceFlinger类的成员变量mVisibleRegionsDirty的值设置为true,以表示我们需要重新计算现存的各个应用程序窗口的可见区域。

     处理完成系统显示屏的属性变化之后,我们接着向下阅读SurfaceFlinger类的成员函数handleTransactionLocked的最后一行代码:

1
2
     commitTransaction();
}

     这段代码只有一行,即调用SurfaceFlinger类的成员函数commitTransaction来告诉SurfaceFlinger服务,系统显示屏以及各个应用程序窗口的属性变化已经处理完毕,这时候SurfaceFlinger服务就可以切换状态了。在后面的Step 6中,我们再详细分析SurfaceFlinger类的成员函数commitTransaction的实现。


     接下来,我们就继续分析Layer类的成员函数doTransaction的实现,以便可以了解应用程序窗口的属性变化的处理过程。

     Step 3. Layer.doTransaction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
uint32_t Layer::doTransaction(uint32_t flags)
{
     const  Layer::State& front(drawingState());
     const  Layer::State& temp(currentState());
     const  bool sizeChanged = (front.requested_w != temp.requested_w) ||
             (front.requested_h != temp.requested_h);
     if  (sizeChanged) {
         ......
         if  (!isFixedSize()) {
             // we\'re being resized and there is a freeze display request,
             // acquire a freeze lock, so that the screen stays put
             // until we\'ve redrawn at the new size; this is to avoid
             // glitches upon orientation changes.
             if  (mFlinger->hasFreezeRequest()) {
                 // if the surface is hidden, don\'t try to acquire the
                 // freeze lock, since hidden surfaces may never redraw
                 if  (!(front.flags & ISurfaceComposer::eLayerHidden)) {
                     mFreezeLock = mFlinger->getFreezeLock();
                 }
             }
             // this will make sure LayerBase::doTransaction doesn\'t update
             // the drawing state\'s size
             Layer::State& editDraw(mDrawingState);
             editDraw.requested_w = temp.requested_w;
             editDraw.requested_h = temp.requested_h;
             // record the new size, form this point on, when the client request
             // a buffer, it\'ll get the new size.
             setBufferSize(temp.requested_w, temp.requested_h);
             ClientRef::Access sharedClient(mUserClientRef);
             SharedBufferServer* lcblk(sharedClient.get());
             if  (lcblk) {
                 // all buffers need reallocation
                 lcblk->reallocateAll();
             }
         else  {
             // record the new size
             setBufferSize(temp.requested_w, temp.requested_h);
         }
     }
     if  (temp.sequence != front.sequence) {
         if  (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha ==  0 ) {
             // this surface is now hidden, so it shouldn\'t hold a freeze lock
             // (it may never redraw, which is fine if it is hidden)
             mFreezeLock.clear();
         }
     }
     return  LayerBase::doTransaction(flags);
}

      这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中。 


      和SurfaceFlinger服务类似,每一个应用程序窗口在内部也分别使用两个类型为State的成员变量mDrawingState和mCurrentState来描述上一次的渲染状态和下一次的渲染状态。这两个成员变量是从LayerBase类继承下来的,用来描述应用程序窗口的Layer类可以分别通过从父类LayerBase继承下来的成员函数drawingState和currentState来访问它们。注意,这里所说的State类是定义在LayerBase类内部的,而SurfaceFlinger服务使用的State类是定义在SurfaceFlinger类内部的,它们是不一样的。

      Layer类的成员函数doTransaction首先调用从父类LayerBase继承下来的成员函数drawingState和currentState来获得当前正在处理的应用程序窗口的上一次的渲染状态和下一次的渲染状态,并且分别保存在两个类型为State的变量front和temp中。

      State类的成员变量requested_w和requested_h分别用来描述应用程序窗口的宽度和高度,因此,当State变量front的成员变量requested_w和requested_h不等于State变量temp的成员变量requested_w和requested_h时,我们就会得到变量sizeChanged的值等于true,表示当前正在处理的应用程序窗口的大小发生了变化。在分析Layer类的成员函数doTransaction处理应用程序窗口的大小变化时,我们先介绍Layer类的成员变量mFixedSize的含义。

      Layer类的成员变量mFixedSize是一个布尔变量,它的值可以通过Layer类的成员函数isFixedSize来获得。从前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文可以知道,当Android应用程序请求SurfaceFlinger服务分配一块图形缓冲区时, Layer类的成员函数requestBuffer就会被调用。这时候Android应用程序会传递两个参数reqWidth和reqHeight过来,表示请求分配的图形缓冲区的宽度和高度。这两个参数是可以同时等于0的,表示使用默认的宽度和高度值来创建所请求的图形缓冲区。这两个默认的宽度和高度值即等于当前所处理的应用程序窗口的宽度和高度值,而后者的宽度和高度值是在其创建的时候指定的,这一点可以参考Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文。

      Layer类的成员函数requestBuffer的参数reqWidth和reqHeight的值等于0意味着什么呢?从前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文还可以知道,Android应用程序在请求SurfaceFlinger服务分配一块图形缓冲区之前,会通过在Surface类内部定义的BufferInfo类的成员函数validateBuffer来检查当前所处理的应用程序窗口的大小是否发生了变化。如果发生了变化,那么Android应用程序就会忽略掉缓存自己一侧的图形缓冲区,而去SurfaceFlinger服务请求新的图形缓冲区,因为那些缓存的图形缓冲区由于与它们所关联的应用程序窗口大小发生了变化而变为无效了。但是有一种特殊情况,在Android应用程序这一侧,用来描述应用程序窗口的Surface类可以不维护应用程序窗口的大小值。在这种情况下,Surface类就会将与它所关联的应用程序窗口的大小值设置为0,这意味着Android应用程序每次为这个应用程序窗口向SurfaceFlinger服务请求分配图形缓冲区之前,都认为这个应用程序窗口的大小值没有发生变化,同时传递给Layer类的成员函数requestBuffer的参数reqWidth和reqHeight的值会等于0。事实上,一个应用程序窗口的大小是随时都可以发生变化的,比如,我们可以通过调用用来在Android应用程序和SurfaceFlinger服务建立连接的一个类型为Client的Binder对象的成员函数setState来改变一个应用程序窗口的大小,而一旦一个应用程序窗口的大小发生了变化,我们正在分析Layer类的成员函数doTransaction就会被调用。

     从上面的分析就可以得出一个结论,Layer类的成员函数doTransaction在处理应用程序窗口大小变化时,需要考虑Android应用程序每次在为该应用程序窗口向SurfaceFlinger服务请求分配图形缓冲区之前,是否有能力去判断之前为该应用程序窗口缓存的图形缓冲区的有效性。如果没有的话,那么Layer类的成员函数doTransaction就需要将为该应用程序窗口缓存的图形缓冲区设置为无效,以便以后Android应用程序可以请求SurfaceFlinger服务分配新的、大小正确的图形缓冲区。从前面的分析还可以知道,当Android应用程序没有能力去判断之前为一个应用程序窗口所缓存的图形缓冲区的有效性时,那么之前在请求分配这些图形缓冲区时,传递给Layer类的成员函数requestBuffer的参数reqWidth和reqHeight的值就会等于0,这时候Layer类的成员函数requestBuffer就会将Layer类的成员变量mFixedSize的值设置为false。

     接下来,我们就分别根据Layer类的成员变量mFixedSize是等于true还是false来分析Layer类的成员函数doTransaction处理一个应用程序窗口大小发生变化的过程。

     当Layer类的成员变量mFixedSize的值等于true时,Layer类的成员函数doTransaction的处理很简单,它只是调用另外一个成员函数setBufferSize来将新的应用程序窗口大小记录下来,即保存在Layer类的成员变量mWidth和mHeight中。

     当Layer类的成员变量mFixedSize的值等于false时,由于Android应用程序没有能力去判断之前为一个应用程序窗口所缓存的图形缓冲区的有效性,因此,Layer类的成员函数doTransaction除了会调用外一个成员函数setBufferSize来将新的应用程序窗口大小记录下来之外,还会通过一个SharedBufferServer对象的成员函数reallocateAll来将为当前正在处理的应用程序窗口所缓存的图形缓冲区设置为无效。在前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文中,我们已经分析过SharedBufferServer类的作用了,它是用来维护Android应用程序与SurfaceFlinger服务之间的共享UI元数据的,通过它可以将对应的图形缓冲区设置为无效。

     当Layer类的成员变量mFixedSize的值等于false时,Layer类的成员函数doTransaction还会提前将成员变量mCurrentState所描述的一个State对象的成员变量requested_w和requested_h的值保存到成员变量mDrawingState所描述的一个State对象的成员变量requested_w和requested_h中去,这是为了避免后面调用父类LayerBase的成员函数doTransaction时,会返回一个Layer::eVisibleRegion位不等于0的标志值给前面的Step 2,而这将会导致SurfaceFlinger服务马上重新计算各个应用程序窗口的可见区域。现在不返回一个Layer::eVisibleRegion位不等于0的标志值给前面的Step 2,就会等到下次渲染当前正在处理的应用程序窗口时再重新计算各个应用程序窗口的可见区域。

     此外,当Layer类的成员变量mFixedSize的值等于false时,Layer类的成员函数doTransaction还会检查系统显示屏是否正处于冻结的状态,这是通过调用Layer类的成员变量mFlinger所指向的SurfaceFlinger服务的成员函数hasFreezeRequest来实现的。如果系统显示屏处于冻结的状态中,并且当前正在处理的应用程序窗口处于可见状态,即变量front所描述的State对象的成员变量flags的ISurfaceComposer::eLayerHidden位等于0,那么Layer类的成员函数doTransaction还会请求获得一个用来冻结系统显示屏的类型为FreezeLock的锁,并且将这个锁保存在Layer类的成员变量mFreezeLock中。这样就可以等到下次渲染当前正在处理的应用程序窗口时,再来刷新系统的UI。

     State类的另外一个成员变量sequence用来描述一个应用程序窗口的其它属性是否发生过变化,例如,X、Y和Z轴位置、透明度等除了大小之外的属性。每当这些属性发生了变化,那么State类的成员变量sequence的值就会比原来增加1。Layer类的成员函数doTransaction再接下来就通过比较变量temp和front所描述的State对象的成员变量sequence的值是否相等来判断当前正在处理的应用程序窗口的其它属性是否发生变化。如果发生过变化,并且当前正在处理的应用程序窗口处于不可见状态或者处于完全透明的状态,那么Layer类的成员函数doTransaction就会释放之前所持有的用来冻结系统显示屏的一个类型为FreezeLock的锁,这是通过调用Layer类的成员变量mFreezeLock所指向的一个FreezeLock对象的成员函数clear来实现的,这样就可以避免阻止SurfaceFlinger服务更新系统UI。

     最后,Layer类的成员函数doTransaction调用父类LayerBase的成员函数doTransaction来继续处理应用程序窗口的属性变化。

     Step 4. LayerBase.doTransaction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
uint32_t LayerBase::doTransaction(uint32_t flags)
{
     const  Layer::State& front(drawingState());
     const  Layer::State& temp(currentState());
     if  ((front.requested_w != temp.requested_w) ||
         (front.requested_h != temp.requested_h))  {
         // resize the layer, set the physical size to the requested size
         Layer::State& editTemp(currentState());
         editTemp.w = temp.requested_w;
         editTemp.h = temp.requested_h;
     }
     if  ((front.w != temp.w) || (front.h != temp.h)) {
         // invalidate and recompute the visible regions if needed
         flags |= Layer::eVisibleRegion;
     }
     if  (temp.sequence != front.sequence) {
         // invalidate and recompute the visible regions if needed
         flags |= eVisibleRegion;
         this ->contentDirty =  true ;
         // we may use linear filtering, if the matrix scales us
         const  uint8_t type = temp.transform.getType();
         mNeedsFiltering = (!temp.transform.preserveRects() ||
                 (type >= Transform::SCALE));
     }
     // Commit the transaction
     commitTransaction();
     return  flags;
}

      这个函数定义在文件frameworks/base/services/surfaceflinger/LayerBase.cpp中。 


      与前面的Step 3一样,LayerBase类的成员函数doTransaction首先通过成员函数drawingState和currentState来获得用来描述当前正在处理的应用程序窗口的当前渲染状态的一个State对象front和下一次渲染状态的一个State对象temp,接下来就可以通过这两个State对象的成员变量requested_w和requested_h来判断当前正在处理的应用程序窗口的大小是否发生了变化。如果发生了变化,那么就将新的应用程序窗口大小值保存在用来描述下一次渲染状态的State对象editTemp的成员变量w和h中。

      State类的成员变量requested_w、requested_h和w、h的区别在于,前两者用来描述应用程序窗口的大小,后两者虽然也是用来描述应用程序窗口的大小,不过它们的作用是用来判断是否需要重新计算系统中的各个应用程序窗口的可见区域的。一旦用来描述应用程序窗口的当前渲染状态的State对象front和下一次渲染状态的State对象temp的成员变量w和h不相等,那么就说明需要系统中的各个应用程序窗口的可见区域,这是通过将将返回值flags的Layer::eVisibleRegion位设置为1来实现的。

      LayerBase类的成员函数doTransaction接下来还会继续判断State对象front和temp的成员变量sequence的值是否相等。如果不相等,那么就说明当前正在处理的应用程序窗口的其它属性,例如位置和透明度等发生了变化。在这种情况下,SurfaceFlinger服务也是需要重新计算系统中的各个应用程序窗口的可见区域的。因此,这时候 LayerBase类的成员函数doTransaction就会将它的返回值flags的Layer::eVisibleRegion位设置为1,并且将Layer类的成员变量contentDirty的值设置为true,表示当前正在处理的应用程序窗口的内容是脏的,需要重新渲染。

      LayerBase类的成员函数doTransaction最后还会继续调用另外一个成员函数commitTransaction来结束对当前正在处理的应用程序窗口的属性变化处理,以及将返回值flags返回给前面的Step 2,即SurfaceFlinger类的成员函数handleTransactionLocked。SurfaceFlinger类的成员函数handleTransactionLocked一旦发现这个返回值的Layer::eVisibleRegion位的值为1,那么就会将SurfaceFlinger类的成员变量mVisibleRegionsDirty的值设置为true,以便接下来可以重新计算系统中的各个应用程序窗口的可见区域。

      接下来,我们就继续分析LayerBase类的成员函数commitTransaction的实现。

      Step 5. LayerBase.commitTransaction

1
2
3
void  LayerBase::commitTransaction() {
     mDrawingState = mCurrentState;
}

  这个函数定义在文件frameworks/base/services/surfaceflinger/LayerBase.cpp中。


     由于此时当前正在处理的应用程序窗口的属性变化已经处理完成了,因此,LayerBase类的成员函数commitTransaction就将用来描述下一次渲染状态的成员变量mCurrentState所描述的一个State对象保存在另外一个用来描述当前渲染状态的成员变量mDrawingState中去。

     这一步执行完成之后,返回到前面的Step 2中,即SurfaceFlinger类的成员函数handleTransactionLocked中,这时候系统显示屏的属性变化以及各个应用程序的属性变化就都已经处理完成了,SurfaceFlinger类的成员函数handleTransactionLocked最后就会调用另外一个成员函数commitTransaction来结束整个属性变化处理过程。

     Step 6. SurfaceFlinger.commitTransaction

1
2
3
4
5
6
void  SurfaceFlinger::commitTransaction()
{
     mDrawingState = mCurrentState;
     mResizeTransationPending =  false ;
     mTransactionCV.broadcast();
}

     这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。


     前面提到,SurfaceFlinger类的成员变量mDrawingState用来描述SufaceFlinger服务的当前状态,而成员变量mCurrentState用来描述SufaceFlinger服务的下一个状态,由于这时候SufaceFlinger服务的状态变化已经处理完成了,因此,SurfaceFlinger类的commitTransaction就将成员变量mCurrentState所指向的一个State对象保存在成员变量mDrawingState中,接着又将成员变量mResizeTransationPending的值设置为false,表示SufaceFlinger服务已经处理过系统显示屏或者应用程序窗口的大小变化了,最后调用成员变量mTransactionCV所描述的一个条件变量的成员函数broadcast来通知其它线程,例如Binder线程,SufaceFlinger服务已经处理完成一次属性变化了。

     至此,我们就分析完成系统显示屏以及应用程序窗口的属性变化的处理过程,接下来我们继续分析各个应用程序窗口是如何设置它们当前所要渲染的图形缓冲区的,即SurfaceFlinger类的成员函数handlePageFlip的实现。

     2. handlePageFlip

     SurfaceFlinger类的成员函数handlePageFlip是用来让各个应用程序窗口设置它们当前所要渲染的图形缓冲区的,在这个过程中,SurfaceFlinger服务还会计算各个应用程序窗口的可见区域,如图3所示。

1350923670_1694.jpg

图3 应用程序窗口设置当前所要渲染的图形缓冲区的过程

       这个过程可以分为7个步骤,接下来我们就详细分析每一个步骤。

       Step 1. SurfaceFlinger.handlePageFlip

1
2
3
4
void  SurfaceFlinger::handlePageFlip()
{
     bool visibleRegions = mVisibleRegionsDirty;
     LayerVector& currentLayers = const_cast<layervector&>(            mDrawingState.layersSortedByZ);    visibleRegions |= lockPageFlip(currentLayers);         const  DisplayHardware& hw = graphicPlane( 0 ).displayHardware();         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>visibleRegionScreen.isEmpty())                    mVisibleLayersSortedByZ.add(currentLayers[i]);            }#ifdef USE_COMPOSITION_BYPASS            sp<layerbase> bypassLayer;             const  size_t numVisibleLayers = mVisibleLayersSortedByZ.size();             if  (numVisibleLayers ==  1 ) {                 const  sp<layerbase>& candidate(mVisibleLayersSortedByZ[ 0 ]);                 const  Region& visibleRegion(candidate->visibleRegionScreen);                 const  Region reminder(screenRegion.subtract(visibleRegion));                 if  (reminder.isEmpty()) {                     // fullscreen candidate!                    bypassLayer = candidate;                }            }            setBypassLayer(bypassLayer);#endif            mWormholeRegion = screenRegion.subtract(opaqueRegion);            mVisibleRegionsDirty = false;        }    unlockPageFlip(currentLayers);    mDirtyRegion.andSelf(screenRegion);}</layerbase></layerbase></count></layervector&>

       这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。


       函数首先将SurfaceFlinger类的成员变量mVisibleRegionsDirty的值保存在变量visibleRegions中。从前面第1部分的内容可以知道,当SurfaceFlinger服务在处理系统显示屏以及各个应用程序窗口的属性变化时,如果发现需要重新计算各个应用程序窗口的可见区域,那么就会将SurfaceFlinger类的成员变量mVisibleRegionsDirty的值设置为true。

      函数接下来通过SurfaceFlinger类的成员变量mDrawingState所描述的一个State对象的成员变量layersSortedByZ来获得系统当前所有的应用程序窗口,并且保存在一个类型为LayerVector的向量currentLayers中。有了系统当前所有的应用程序窗口之后,就可以通过调用SurfaceFlinger类的成员函数lockPageFlip来让它们设置自己当前所要渲染
的图形缓冲区。在后面的Step 2中,我们再详细分析SurfaceFlinger类的成员函数lockPageFlip的实现。

     系统中的各个应用程序窗口在设置自己当前所要渲染的图形缓冲区的过程中,有可能会改变自己的大小。在这种情况下,它们就会通过SurfaceFlinger类的成员函数lockPageFlip来返回一个true值来给SurfaceFlinger类的成员函数handlePageFlip,这时候得到的变量visibleRegions的值就会等于true,表示需要重新计算各个应用程序窗口的可见区域。

     综合上述情况,无论变量visibleRegions的值是由于什么原因等于true,都说明SurfaceFlinger服务需要重新计算各个应用程序窗口的可见区域,这个计算的工作是通过调用SurfaceFlinger类的另外一个成员函数computeVisibleRegions来完成的。在后面的Step 5中,我们再详细分析SurfaceFlinger类的成员函数computeVisibleRegions的实现。

     SurfaceFlinger类的成员函数computeVisibleRegions在计算完成各个应用程序窗口的可见区域之后,会得到一个全局不透明区域,保存在输出参数opaqueRegion中。这个全局不透明区域就是接下来需要渲染的,一般情况下,它的大小就应该等于显示屏的大小,即变量screenRegion所描述的区域。在异常情况下,可能会导致显示屏区域screenRegion大于全局不透明区域opaqueRegion,这时候前者减去后者就可以得到一些称为“虫洞”的区域,它们保存在SurfaceFlinger类的成员变量mWormholeRegion中。由于这些虫洞区域不会被各个应用程序窗口覆盖,因此,SurfaceFlinger服务需要对它们进行特殊处理,即以一种特殊的方式来渲染它们。在后面的第4部分内容中,我们就会看到SurfaceFlinger服务是通过调用SurfaceFlinger类的成员函数drawWormhole来渲染这些虫洞的。

     SurfaceFlinger类的成员函数computeVisibleRegions在计算完成各个应用程序窗口的可见区域之后,各个应用程序窗口的可见区域就会保存在用来描述它们的一个LayerBase对象的成员变量visibleRegionScreen中,因此,SurfaceFlinger类的成员函数handlePageFlip就会通过这个成员变量来排除掉那些可见区域为空的应用程序窗口,并且将所有可见区域不为空的应用程序窗口按照它们的Z轴大小保存在SurfaceFlinger类的成员变量mVisibleLayersSortedByZ所描述的一个向量中。

     经过前面的操作之后,SurfaceFlinger类的成员函数handlePageFlip就可以将成员变量mVisibleRegionsDirty的值设置为false了,因为这时候系统中各个应用程序窗口的可见区域都已经重新计算过了。

     SurfaceFlinger类的成员函数handlePageFlip最后就调用另外一个成员函数unlockPageFlip来让各个应用程序窗口执行一些善后的工作,例如,让各个应用程序窗口检查自己的可见区域是否等于空,如果等于空的话,那么就需要将它们之前所获得的用来冻结显示屏的锁释放掉,以避免阻止SurfaceFlinger服务渲染系统UI。在后面的Step 6中,我们再详细分析SurfaceFlinger类的成员函数unlockPageFlip的实现。

     SurfaceFlinger类的成员函数handlePageFlip最后还需要做的另外一件事情是将设置系统的脏区域,这个脏区域保存在SurfaceFlinger类的成员变量mDirtyRegion中,它同样是作为一个输出参数来传递给SurfaceFlinger类的成员函数computeVisibleRegions的,以便后者在计算各个应用程序窗口的可见区域时,可以将获得的系统脏区域保存在它里面。我们同样是在后面的Step 5中分析SurfaceFlinger类的成员函数computeVisibleRegions的实现时,再看看系统的脏区域是如何计算的。

     有一种特殊情况,即SurfaceFlinger类的成员函数handlePageFlip在重新计算完成各个应用程序窗口的可见区域后,如果满足以下三个条件:

    1. SurfaceFlinger服务在编译时指定了宏USE_COMPOSITION_BYPASS;

    2. 当前要渲染的应用程序窗口只有一个,即SurfaceFlinger类的成员变量mVisibleLayersSortedByZ所描述的一个向量的大小等于1;

    3. 当前要渲染的一个唯一的应用程序窗口的可见区域的大小不为空。

    那么SurfaceFlinger服务就会直接将这个唯一需要渲染的应用程序窗口的图形缓冲区渲染到硬件帧缓冲区中去,以跳过后面的合成各个应用程序窗口的图形缓冲区的操作。在这种情况下,这个唯一需要渲染的应用程序窗口会通过SurfaceFlinger类的成员函数setBypassLayer记录起来。

     SurfaceFlinger类的成员函数setBypassLayer的实现如下所示:

1
void  SurfaceFlinger::setBypassLayer( const  sp<layerbase>& layer){     // if this layer is already the bypass layer, do nothing    sp<layer> cur(mBypassLayer.promote());    if (mBypassLayer == layer) {        if (cur != NULL) {            cur->updateBuffersOrientation();        }        return;    }    // clear the current bypass layer    mBypassLayer.clear();    if (cur != 0) {        cur->setBypass(false);        cur.clear();    }    // set new bypass layer    if (layer != 0) {        if (layer->setBypass(true)) {            mBypassLayer = static_cast<layer*>(layer.get());        }    }}</layer*></layer></layerbase>

      这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。


      从这里就可以看出,在指定了编译宏USE_COMPOSITION_BYPASS的情况下,SurfaceFlinger服务唯一需要渲染的应用程序窗口就保存在SurfaceFlinger类的一个类型为Layer的成员变量mBypassLayer中,并且会调用用来这个Layer对象的成员函数setBypass来将它里面的一个成员变量mBypassState的值设置为true。在后面的第3部分内容中,我们再详细分析这个唯一需要渲染的应用程序窗口的图形缓冲区是如何直接渲染到硬件帧缓冲区中去的。

     接下来,我们就继续分析SurfaceFlinger类的成员函数lockPageFlip、computeVisibleRegions和unlockPageFlip的实现,以便可以分别了解各个应用程序窗口是如何设置自己当前需要渲染的图形缓冲区的、SurfaceFlinger服务是如何计算各个应用程序窗口的可见区域的,以及各个应用程序窗口的可见区域计算完成之后的善后工作是什么。

     Step 2. SurfaceFlinger.lockPageFlip

1
2
3
4
5
bool SurfaceFlinger::lockPageFlip( const  LayerVector& currentLayers)
{
     bool recomputeVisibleRegions =  false ;
     size_t count = currentLayers.size();
     sp<layerbase>  const * layers = currentLayers.array();     for  (size_t i= 0  ; i<count ; i++) {         const  sp<layerbase>& layer(layers[i]);        layer->lockPageFlip(recomputeVisibleRegions);    }     return  recomputeVisibleRegions;}</layerbase></layerbase>

       这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。

       在前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划的一系列文章中,我们假设在SurfaceFlinger服务这一侧,每一个应用程序窗口都是使用一个Layer对象来描述,这些Layer对象都是从LayerBase继承下来的,因此它们可以保存在一个类型为LayerBase的向量中。

       从前面的调用过程可以知道,参数currentLayers里面保存的一系列LayerBase对象正是用来描述系统当前的各个应用程序窗口的,SurfaceFlinger类的成员函数lockPageFlip依次调用这些LayerBase对象的成员函数lockPageFlip来让它们设置当前需要渲染的图形缓冲区。由于前面我们假设这些LayerBase对象的实际类型为Layer,因此,前面调用的实际上就是Layer类的成员函数lockPageFlip。接下来,我们就继续分析Layer类的成员函数lockPageFlip的实现。

       Step 3. Layer.lockPageFlip

       这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中。在前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文中,我们已经分析过这个函数的实现了,不过当时分析得不是太完整,现在我们再重新来阅读一下。这个函数比较长,我们分段来阅读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void  Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
     ClientRef::Access sharedClient(mUserClientRef);
     SharedBufferServer* lcblk(sharedClient.get());
     if  (!lcblk) {
         // client died
         recomputeVisibleRegions =  true ;
         return ;
     }
     ssize_t buf = lcblk->retireAndLock();
     if  (buf == NOT_ENOUGH_DATA) {
         // NOTE: This is not an error, it simply means there is nothing to
         // retire. The buffer is locked because we will use it
         // for composition later in the loop
         return ;
     }
     if  (buf < NO_ERROR) {
         LOGE(\"retireAndLock() buffer index (%d) out of range\",  int (buf));
         mPostedDirtyRegion.clear();
         return ;
     }
     // we retired a buffer, which becomes the new front buffer
     if  (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
         LOGE(\"retireAndLock() buffer index (%d) out of range\",  int (buf));
         mPostedDirtyRegion.clear();
         return ;
     }

      这段代码首先通过Layer类的成员变量mUserClientRef来获得一个SharedBufferServer对象lcblk。这个SharedBufferServer对象lcblk是用来描述Android应用程序与SurfaceFlinger服务之间的共享UI元数据,通过调用它的成员函数retireAndLock,Layer类的成员函数lockPageFlip就可以知道当前需要渲染的图形缓冲区的编号。有了这个编号之后,Layer类的成员函数lockPageFlip就可以在成员变量mBufferManager所描述的一个BufferManager中找到一个对应的图形缓冲区。


      这段代码接着将当前需要渲染的图形缓冲区的编号保存在Layer类的成员变量mBufferManager所描述的一个BufferManager中,这是通过调用这个BufferManager的成员函数setActiveBufferIndex来实现的。在接下来的Step 4中,我们就可以通过这个BufferManager的成员函数getActiveBufferIndex来重新获得这个编号,以便可以找到对应的图形缓冲区。

      我们接着往下阅读代码:

1
sp<graphicbuffer> newFrontBuffer(getBuffer(buf)); if  (newFrontBuffer != NULL) {     // get the dirty region    // compute the posted region    const Region dirty(lcblk->getDirtyRegion(buf));    mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );    // update the layer size and release freeze-lock    const Layer::State& front(drawingState());    if (newFrontBuffer->getWidth()  == front.requested_w &&        newFrontBuffer->getHeight() == front.requested_h)    {        if ((front.w != front.requested_w) ||            (front.h != front.requested_h))        {            // Here we pretend the transaction happened by updating the            // current and drawing states. Drawing state is only accessed            // in this thread, no need to have it locked            Layer::State& editDraw(mDrawingState);            editDraw.w = editDraw.requested_w;            editDraw.h = editDraw.requested_h;            // We also need to update the current state so that we don\'t            // end-up doing too much work during the next transaction.            // NOTE: We actually don\'t need hold the transaction lock here            // because State::w and State::h are only accessed from            // this thread            Layer::State& editTemp(currentState());            editTemp.w = editDraw.w;            editTemp.h = editDraw.h;            // recompute visible region            recomputeVisibleRegions = true;        }        // we now have the correct size, unfreeze the screen        mFreezeLock.clear();    }    // get the crop region    setBufferCrop( lcblk->getCrop(buf) );    // get the transformation    setBufferTransform( lcblk->getTransform(buf) );} else {    // this should not happen unless we ran out of memory while    // allocating the buffer. we\'re hoping that things will get back    // to normal the next time the app tries to draw into this buffer.    // meanwhile, pretend the screen didn\'t update.    mPostedDirtyRegion.clear();}</graphicbuffer>

       这段代码首先是用来设置当前正在处理的应用程序窗口的脏区域、纹理坐标和旋转方向的。


       一个应用程序窗口当前的脏区域、纹理坐标,以及旋转方向是保存在一块匿名共享内存中的,用来在Android应用程序和SurfaceFlinger服务之间共享UI元数据。这块匿名共享内存就是使用前面所获得的SharedBufferServer对象lcblk来描述的,即我们可以通过调用SharedBufferServer对象lcblk的成员函数getDirtyRegion、getCrop以及getTransform来获得当前正在处理的应用程序窗口的脏区域、纹理坐标以及旋转方向。其中,脏区域保存在Layer类的成员变量mPostedDirtyRegion中,纹理坐标通过调用父类LayerBase的成员函数setBufferCrop保存在其成员变量mBufferCrop中,而旋转方向通过调用父类LayerBase的成员函数setBufferTransform保存在其成员变量mBufferTransform中。

       从前面的执行过程可以知道,变量buf保存的是当前需要渲染的图形缓冲区的编号。当我们以这个编号为参数来调用Layer类的成员函数getBuffer时,就可以得到一个对应的图形缓冲区。这个图形缓冲区使用一个GraphicBuffer对象来描述,并且保存在变量newFrontBuffer中。

       当前需要渲染的图形缓冲区newFrontBuffer的大小可以通过调用它的成员函数getBounds来获得。我们把当前需要渲染的图形缓冲区的大小与当前正在处理的应用程序窗口的脏区域执行一个相交操作之后,才可以得到最终需要渲染的脏区域。这个最终需要渲染的脏区域就保存在前面提到的Layer类的成员变量mPostedDirtyRegion中。

       这段代码接下来还会检查是否需要修改当前正在处理的应用程序窗口的大小。本来修改应用程序窗口的大小是在处理应用程序窗口的属性变化时进行的,如前面第1部分的内容所示。但是在前面第1部分的内容的Step 3中提到,有一种特殊情况,即Android应用程序在请求SurfaceFlinger服务分配图形缓冲区时,传递过来的图形缓冲区的宽度和高度值等于0。在这种情况下,Layer类的成员函数doTransaction会暂时忽略掉应用程序窗口当前所发生的大小变化,而是等到该应用程序窗口大小发生了变化后所申请分配的新图形缓冲区需要渲染时,再来修改它的大小。

       从前面第1部分的内容的Step 3可以知道,调用Layer类的成员函数drawingState所获得的一个State对象是用来描述一个应用程序窗口的当前渲染状态的,而这个State对象的成员变量requested_w和requested_h描述的就正好是该应用程序窗口的最新宽度和高度值。如果一个应用程序窗口的最新宽度和高度值正好等于它当前所要渲染的图形缓冲区newFrontBuffer的宽度和高度值,那么就说明它当前所要渲染的图形缓冲区newFrontBuffer就正好是在它的大小发生变化之后所申请分配的,因此,这时候Layer类的成员函数lockPageFlip就需要继续检查它之前是否有被忽略掉的大小变化未被处理。如果有的话,现在就是时候对它的大小变化进行处理了。

      从前面第1部分的内容的Step 4可以知道,如果用来描述一个应用程序窗口的当前渲染状态的一个State对象的两个成员变量w和h,与另外两个成员变量requested_w和requested_h不相等的话,那么就说明该应用程序窗口有被忽略掉的大小变化未被处理,因此,在这种情况下,Layer类的成员函数lockPageFlip就会分别将该State对象的成员变量requested_w和requested_h的值分别保存在成员变量w和h中,同时还会保存用来描述该应用程序窗口的下一次渲染状态的一个State对象的成员变量w和h中,以表示前面被忽略掉的窗口大小变化已经得到处理了。

      由于每当有应用程序窗口的大小发生变化之后,SurfaceFlinger服务都需要重新计算各个应用程序窗口的可见区域,因此,Layer类的成员函数lockPageFlip在处理了当前正在处理的应用程序窗口上一次被忽略掉的大小变化之后,需要将输出参数recomputeVisibleRegions的值设置为true,以便可以用来通知SurfaceFlinger服务更新各个应用程序窗口的可见区域,如前面的Step 1所示。

      此外,Layer类的成员函数lockPageFlip在处理了当前正在处理的应用程序窗口上一次被忽略掉的大小变化之后,如果之前获取过用来冻结系统显示屏的锁,那么现在也是时间释放这个锁了,以避免阻止SurfaceFlinger服务接下来重新渲染系统的UI。这个用来冻结系统显示屏的锁是保存在Layer类的成员变量mFreezeLock中的,通过调用它的成员函数clear就可以释放它所描述的用来冻结系统显示屏的锁。

      我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
     if  (lcblk->getQueuedCount()) {
         // signal an event if we have more buffers waiting
         mFlinger->signalEvent();
     }
     /* 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 );
}

       这段代码首先检查当前正在处理的应用程序窗口是否还有其它图形缓冲区在等待被渲染。如果有的话,那么就会通过Layer类的成员变量mFlinger的成员函数signalEvent来向SurfaceFlinger服务的UI渲染线程的消息队列发送另外一个类型为MessageQueue::INVALIDATE的消息,以便SurfaceFlinger服务的UI渲染线程在刷新了当前的系统UI之后,可以马上再次刷新系统的UI。


       执行完成以上操作之后,Layer类的成员函数lockPageFlip就调用另外一个成员函数reloadTexture来为当前正在处理的应用程序窗口加载新的纹理,以便接下来可以将这个新的纹理渲染合成和渲染到硬件帧缓冲区中去。

       接下来, 我们就继续分析Layer类的成员函数reloadTexture是如何获得应用程序窗口即将要合成和渲染的纹理的。

       Step 4. Layer.reloadTexture

1
2
3
void  Layer::reloadTexture( const  Region& dirty)
{
     sp<graphicbuffer> buffer(mBufferManager.getActiveBuffer());     if  (buffer == NULL) {         // this situation can happen if we ran out of memory for instance.        // not much we can do. continue to use whatever texture was bound        // to this context.        return;    }    if (mGLExtensions.haveDirectTexture()) {        EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());        if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) {            // not sure what we can do here...            goto slowpath;        }    } else {slowpath:        GGLSurface t;        if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) {            status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);            LOGE_IF(res, \"error %d (%s) locking buffer %p\",                    res, strerror(res), buffer.get());            if (res == NO_ERROR) {                mBufferManager.loadTexture(dirty, t);                buffer->unlock();            }        } else {            // we can\'t do anything        }    }}</graphicbuffer>

       这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中。

       函数首先调用Layer类的成员变量mBufferManager所描述的一个BufferManager对象的成员函数getActiveBuffer来获得当前正在处理的应用程序窗口接下来需要渲染的图形缓冲区,并且保存在变量buffer中。

       函数接着调用Layer类的成员变量mGLExtensions所描述的一个GLExtensions对象的成员函数haveDirectTexture来判断系统是否支持在硬件上直接创建纹理对象。如果支持的话,那么就调用Layer类的成员变量mBufferManager所描述的一个BufferManager对象的成员函数initEglImage来根据图形缓冲区buffer的内容在硬件上直接创建一个纹理对象。如果这个纹理对象创建成功的话,那么就会保存在Layer类的成员变量mBufferData所描述的一个BufferData中。

       如果系统不支持在硬件上直接创建纹理对象,或者前面在硬件上直接创建纹理对象失败,那么函数就会退而求之,转换为用软件的方式来根据图形缓冲区buffer的内容来创建一个纹理对象,这是通过调用Layer类的成员变量mBufferManager所描述的一个BufferManager对象的成员函数loadTexture来实现的。创建出来的这个纹理对象就保存在Layer类的成员变量mFailoverTexture中。不过,能够使用软件的方式来创建纹理对象的一个前提是图形缓冲区buffer的内容可以通过软件的方式来读取,即它的成员变量usage的GRALLOC_USAGE_SW_READ_MASK位等于1。在这种情况下,我们就可以调用图形缓冲区buffer的成员函数lock来获得它的内容访问地址,并且保存在GGLSurface对象t,以便接下来可以通过这个GGLSurface对象t来创建一个纹理对象。

       Layer类的成员函数reloadTexture首选在硬件上创建纹理对象,如果这种方式不可行,那么再使用软件方式来创建纹理对象。这种技术在计算机领域中就称为失效备援技术,即Failover技术。

       这一步执行完成之后,返回到前面的Step 1中,即SurfaceFlinger类的成员函数handlePageFlip中,接下来就会继续调用SurfaceFlinger类的另外一个成员函数computeVisibleRegions来计算各个应用程序窗口的可见区域。

       Step 5. SurfaceFlinger.computeVisibleRegions

       这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,主要用来计算各个应用程序窗口的可见区域。在分析这个函数的实现之前,我们首先解释一些与应用程序窗口相关的概念:可见区域(Visible Region)、透明区域(Transparent Region)、半透明区域(Translucent Region)、完全不透明区域(Opaque Region)和被覆盖区域(Covered Region)。

       假设一个应用程序窗口的宽度和高度分别为w和h,如图4所示:

1351182166_9415.jpg

图4  应用程序窗口的可见区域

      那么我们就可以将由(0,0)、(0, w)、(0, h)和(w,h)四个点组成的区域称为应用程序窗口的可见区域。

      接下来,我们可以在一个应用程序窗口的可见区域挖一个洞出来,如图5所示:

1351182475_9224.jpg

图5 应用程序窗口的透明区域

     这时候应用程序窗口真正的可见区域就需要减去中间被挖出来的洞。这个被挖出来的洞就称为应用程序窗口的透明可见区域。

     如果应用程序窗口的可见区域的Alpha通道大于0并且小255,那么我们就认为应用程序窗口的可见区域是半透明的。有两种极端情况,即当应用程序窗口的可见区域的Alpha通道等于0或者255的时候。当等于0的时候,我们就认为应用程序窗口的可见区域是透明的,就如图5所示的洞一样,而当等于255的时候,我们就认为应用程序窗口的可见区域是完全不透明的。

     上面我们讨论的应用程序窗口的可见区域是基于单个应用程序窗口而言的,当多个应用程序窗口叠加在一起的时候,在讨论一个应用程序窗口的可见区域的时候,就需要考虑位于它上面的其它应用程序窗口的可见区域的影响了。注意,一个应用程序窗口的可见区域只受位于它上面的其它应用程序窗口影响,而不会受到位于它下面的其它的应用程序窗口影响,因此,我们是按照从上到下的顺序来计算系统中各个应用程序窗口的可见区域的。

      为了方便描述,我们假设位于一个应用程序窗口上面的所有应用程序窗口组成了一个整体的可见区域(Above Covered Layers),并且这个可见区域与我们所要讨论的应用程序窗口相交,即它们叠加在一起,如图6所示:

1351184002_1681.jpg

图6 应用程序窗口的被覆盖区域

      由蓝色矩形组成的区域即为上层所有应用程序窗口所组成的一个整体可见区域,这个整体可见区域与下面绿色矩形组成的一个应用程序窗口相交的部分,即由虚线所围成的区域,就是下面的一个应用程序窗口的被覆盖区域。

      一个应用程序窗口的被覆盖区域有可能是半透明的,也有可能是完全不透明的,但是不可能是透明的,如图7所示:

1351184432_9658.jpg

图7 应用程序窗口的被覆盖完全不透明区域

      在原来由虚线围成的区域中,深蓝色的那部分区域就是完全不透明的(Above Opaque  Layers),这时候由绿色矩形组成的应用程序窗口的可见区域除了要减去中间的洞(透明区域)之外,还要减去被覆盖的完全不透明区域,如下图8所示:

1351185130_6816.jpg

图8 应用程序窗口的最终可见区域

     从上面的讨论我们就可以清楚地知道,为了计算一个应用程序窗口的最终可见区域,我们需要知道:

     1. 应用程序窗口的左上角位置,以及宽度和高度,以便可以获得应用程序窗口的原始可见区域。

     2. 应用程序窗口的透明区域。

     3. 应用程序窗口的被覆盖完全不透明区域。

     用第1步到的原始可见区域减去第2步的透明区域和第3步的被覆盖完全不透明区域,就可以得到一个应用程序窗口的最终可见区域。

     为了获得第3步的被覆盖完全不透明区域,我们在计算一个应用程序窗口的最终可见区域的过程中,还需要将此前得到的应用程序窗口的完全不透明区域组合起来,形成一个覆盖完全不透明区域(Above Opaque  Layers),因此,我们还需要知道:

     4. 应用程序窗口的完全不透明区域。

     此外,由于一个应用程序窗口的被覆盖半透明区域是需要与上层的应用程序窗口可见区域执行混合计算的,因此,我们在计算系统中各个应用程序窗口的可见区域的过程中,还需要将所有上层的应用程序窗口可见区域组合起来形成一个覆盖区域(Above Covered Layers)。

     有了这些背景知识之后,接下来我们就可以分析SurfaceFlinger类的成员函数computeVisibleRegions的实现了。由于SurfaceFlinger类的成员函数computeVisibleRegions的实现比较长,我们分段来阅读:

1
2
3
4
5
6
7
8
9
10
11
void  SurfaceFlinger::computeVisibleRegions(
     LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
{
     const  GraphicPlane& plane(graphicPlane( 0 ));
     const  Transform& planeTransform(plane.transform());
     const  DisplayHardware& hw(plane.displayHardware());
     const  Region screenRegion(hw.bounds());
     Region aboveOpaqueLayers;
     Region aboveCoveredLayers;
     Region dirty;
     bool secureFrameBuffer =  false ;

       这段代码首先计算得到屏幕区域,保存在变量screenRegion中,接着定义了另外两个区域aboveOpaqueLayers,分别用来描述上层覆盖完全不透明区域(Above Opaque  Layers)和上层覆盖区域(Above Covered Layers),最后定义了一个布尔变量secureFrameBuffer,用来描述系统中是否存在界面受到安全保护的应用程序窗口。


      界面受到安全保护的应用程序窗口的内容是不可以在进程间传输的,这个属性主要是应用在屏幕截图中。例如,如果系统中存在一个界面受到安全保护的应用程序窗口,那么我们就不可以请求SurfaceFlinger服务执行截屏功能,因为SurfaceFlinger服务截取下来的屏幕会被传输给请求的进程使用。

     我们接着往下阅读代码:

1
2
3
size_t i = currentLayers.size();
while  (i--) {
     const  sp<layerbase>& layer = currentLayers[i];    layer->validateVisibility(planeTransform);</layerbase>

      这段代码是一个while循环的前面几行。系统中所有需要计算可见区域的应用程序窗口都保存在参数currentLayers所描述的一个向量中。这段代码的while循环就是用来逐个地这些应用程序窗口的可见区域的。注意,这个while是先计算是按照从上到下的顺序来计算系统中各个应用程序窗口的可见区域的。


      在计算一个应用程序窗口layer的可见区域之前,我们首先要验证它的可见性,这是通过调用它的成员函数validateVisibility来实现的,即调用LayerBase类的成员函数validateVisibility来实现的。

      LayerBase类的成员函数validateVisibility的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void  LayerBase::validateVisibility( const  Transform& planeTransform)
{
     const  Layer::State& s(drawingState());
     const  Transform tr(planeTransform * s.transform);
     const  bool transformed = tr.transformed();
     uint32_t w = s.w;
     uint32_t h = s.h;
     tr.transform(mVertices[ 0 ],  0 0 );
     tr.transform(mVertices[ 1 ],  0 , h);
     tr.transform(mVertices[ 2 ], w, h);
     tr.transform(mVertices[ 3 ], w,  0 );
     if  (UNLIKELY(transformed)) {
         // NOTE: here we could also punt if we have too many rectangles
         // in the transparent region
         if  (tr.preserveRects()) {
             // transform the transparent region
             transparentRegionScreen = tr.transform(s.transparentRegion);
         else  {
             // transformation too complex, can\'t do the transparent region
             // optimization.
             transparentRegionScreen.clear();
         }
     else  {
         transparentRegionScreen = s.transparentRegion;
     }
     // cache a few things...
     mOrientation = tr.getOrientation();
     mTransformedBounds = tr.makeBounds(w, h);
     mLeft = tr.tx();
     mTop  = tr.ty();
}

      这个函数定义在文件frameworks/base/services/surfaceflinger/LayerBase.cpp中。

      参数planeTransform用来描述系统显示屏旋转方向,它是一个变换矩阵,而用来描述当前正在处理的应用程序窗口的当前渲染状态的一个State对象s的成员变量transform指向的也是一个变换矩阵,用来描述当前正在处理的应用程序窗口的位置、旋转方向和缩放因子等。将这两者相乘,就可以得到当前正在处理的应用程序窗口相对于系统显示屏的一个变换矩阵tr。

       函数首先计算当前正在处理的应用程序窗口的四个角在显示屏中的位置,并且分别保存在LayerBase的成员变量mVertices所描述的一个数组中,这是通过调用变换矩阵tr的成员函数transform来实现的。

       函数接着判断当前正在处理的应用程序窗口是否被旋转过或者被缩放过。如果是的话,那么前面调用变换矩阵tr的成员函数transformed的返回值就会等于true,即变量transformed的等于true。在这种情况下,函数就要相应地对当前正在处理的应用程序窗口的透明区域进行旋转或者缩放。但是有一种特殊情况,即当当前正在处理的应用程序窗口被旋转和缩放得不规则时,这时候对应用程序窗口的透明区域进行旋转或者缩放就会很复杂,于是函数就干脆将它的透明区域忽略掉。判断当前正在处理的应用程序窗口是否被旋转和缩放得不规则是通过调用变换矩阵tr的成员函数preserveRects来实现的,当它的返回值等于true的时候,就说明当前正在处理的应用程序窗口是否被旋转和缩放之后还是规则,否则就是不规则的。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/1242719,如需转载请自行联系原作者
目录
相关文章
|
19天前
|
前端开发 编解码 数据格式
浅谈响应式编程在企业级前端应用 UI 开发中的实践
浅谈响应式编程在企业级前端应用 UI 开发中的实践
20 0
浅谈响应式编程在企业级前端应用 UI 开发中的实践
|
28天前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
19 1
|
1月前
|
Web App开发 数据采集 前端开发
纯技术讨论:如何让 SAP UI5 应用无法被别人在浏览器里调试 - 这种做法不推荐试读版
纯技术讨论:如何让 SAP UI5 应用无法被别人在浏览器里调试 - 这种做法不推荐试读版
15 0
|
1月前
|
XML 存储 数据格式
SAP UI5 控件 customData 属性的应用介绍
SAP UI5 控件 customData 属性的应用介绍
31 0
|
2月前
|
搜索推荐 Android开发 iOS开发
探析安卓与iOS系统的优劣
【2月更文挑战第7天】安卓与iOS是当今手机市场上最主流的两款操作系统,各有优劣。本文将从用户体验、开放程度、生态系统等方面对两者进行深入探析,以期帮助读者更好地了解它们的特点。
|
2月前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
35 4
|
1天前
|
Android开发
Android 11 添加Service服务SELinux问题
Android 11 添加Service服务SELinux问题
7 1
|
22天前
|
机器学习/深度学习 人工智能 搜索推荐
探索安卓应用中的新趋势:人工智能驱动的智能推荐系统
传统的应用推荐系统已经无法满足用户日益增长的个性化需求。本文将探讨如何通过引入人工智能技术,构建智能推荐系统,为用户提供更加精准、个性化的应用推荐体验,进而提升应用的用户满意度和留存率。
17 0
|
1月前
|
搜索推荐 测试技术 定位技术
基于Android的自助导游系统的设计与实现(论文+源码)_kaic
基于Android的自助导游系统的设计与实现(论文+源码)_kaic
|
1月前
|
搜索推荐 安全 Android开发
安卓与iOS系统的用户体验比较
【2月更文挑战第11天】 在当今移动设备市场上,安卓和iOS系统一直是两大主流操作系统。本文将从用户界面设计、应用生态、系统定制性等方面对安卓和iOS系统进行比较分析,旨在探讨两者的优势和劣势,为用户选择合适的操作系统提供参考。

热门文章

最新文章