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

简介:

    当前正在处理的应用程序窗口的透明区域保存在State对象s的成员变量transparentRegion中,按照上述原理,函数按照以下规则来对它进行处理:

       1. 当变量transformed的等于false时,说明当前正在处理的应用程序窗口的透明区域就不需要进行旋转或者缩放,这时候就可以将这个透明区域保存在LayerBase类的成员变量transparentRegionScreen中。

       2. 当变量transformed的等于true,并且变换矩阵tr的成员函数preserveRects的返回值也等于true时,那么就说明当前正在处理的应用程序窗口的透明区域需要进行旋转或者缩放,这时候通过调用变换矩阵tr的成员函数transform来实现的。 最终得到的透明区域同样是保存在LayerBase类的成员变量transparentRegionScreen中。

       3.  当变量transformed的等于true,并且变换矩阵tr的成员函数preserveRects的返回值等于false时,那么就说明需要忽略掉当前正在处理的应用程序窗口的透明区域,这是通过LayerBase类的成员变量transparentRegionScreen所描述的一个Region对象的成员函数clear来实现的。

       最后,函数就计算当前正在处理的应用程序窗口的方向,左上角位置,以及包含了当前正在处理的应用程序窗口的一个矩形区域,这些值分别保存在在LayerBase类的成员变量mOrientation,mLeft和mTop,以及mTransformedBounds中。

       返回到SurfaceFlinger类的成员函数computeVisibleRegions中,我们继续往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// start with the whole surface at its current location
constLayer::State& s(layer->drawingState());
/*
* opaqueRegion: area of a surface that is fully opaque.
*/
Region opaqueRegion;
/*
* visibleRegion: area of a surface that is visible on screen
* and not fully transparent. This is essentially the layer\'s
* footprint minus the opaque regions above it.
* Areas covered by a translucent surface are considered visible.
*/
Region visibleRegion;
/*
* coveredRegion: area of a surface that is covered by all
* visible regions above it (which includes the translucent areas).
*/
Region coveredRegion;

       这段代码首先获得用来描述当前正在处理的应用程序窗口的当前渲染状态的一个State对象s,接着再定义了三个Region对象opaqueRegion、visibleRegion和coveredRegion,分别用来描述当前正在处理的应用程序窗口的完全不透明区域、可见区域和被覆盖区域。这三个区域的含义和作用如前所述。


        我们继续往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// handle hidden surfaces by setting the visible region to empty
if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
constbool translucent = layer->needsBlending();
constRect bounds(layer->visibleBounds());
visibleRegion.set(bounds);
visibleRegion.andSelf(screenRegion);
if (!visibleRegion.isEmpty()) {
// Remove the transparent area from the visible region
if (translucent) {
visibleRegion.subtractSelf(layer->transparentRegionScreen);
}
// compute the opaque region
constint32_t layerOrientation = layer->getOrientation();
if (s.alpha== 255 && !translucent &&
((layerOrientation & Transform::ROT_INVALID) ==  false )) {
// the opaque region is the layer\'s footprint
opaqueRegion = visibleRegion;
}
}
}

       这段代码用来计算当前正在处理的应用程序窗口的可见区域和完全不透明区域。只有在当前正在处理的应用程序窗口处于可见状态,并且它不是完全透明时,才需要计算这两个区域。当State对象s的成员变量flags的ISurfaceComposer::eLayerHidden位等于0时,就说明当前正在处理的应用程序窗口是处于可见状态的,而当它的另外一个成员变量alpha的值不等于0的时候,就说明当前正在处理的应用程序窗口不是完全透明的。


       我们首先来看当前正在处理的应用程序窗口的可见区域的计算过程。

       LayerBase对象layer的成员函数visibleBounds返回的是包围当前正在处理的应用程序窗口的一个矩形,即LayerBase类的成员变量mTransformedBounds,这是在前面调用LayerBase类的成员函数validateVisibility时计算得到的。函数首先将这个矩形与屏幕区域screenRegion执行一个与操作,以便可以得到当前正在处理的应用程序窗口的初始可见区域visibleRegion。

       当得到当前正在处理的应用程序窗口的初始可见区域visibleRegion不为空时,函数接着判断该窗口是否是半透明的,即它是否要与其它的窗口执行混合操作。如果是半透明的话,那么前面调用LayerBase对象layer的成员函数needsBlending的返回值translucent就会等于true。在这种情况下,我们就需要将应用程序窗口的初始可见区域visibleRegion减去它的透明区域,即LayerBase对象layer的成员变量transparentRegionScreen所描述的区域。这样我们进一步得到当前正在处理的应用程序窗口的可见区域visibleRegion。

       我们接着来看当前正在处理的应用程序窗口的完全不透明区域的计算过程。

       当当前正在处理的应用程序窗口是完全不透明,并且旋转方向也是规则时,那么它的完全不透明区域opaqueRegion就等于前面计算所得到的可见区域visibleRegion。当当前正在处理的应用程序窗口的Alpha通道等于255,即当State对象s的成员变量alpha的值等于255,并且变量translucent的值等于false时,就说明它是完全不透明的,而当当前正在处理的应用程序窗口的旋转方向layerOrientation的Transform::ROT_INVALID位等于0的时候,就说明它的旋转方向是规则的。

       我们继续往下阅读代码:

1
2
3
4
5
6
// Clip the covered region to the visible region
coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
// Update aboveCoveredLayers for next (lower) layer
aboveCoveredLayers.orSelf(visibleRegion);
// subtract the opaque region covered by the layers above us
visibleRegion.subtractSelf(aboveOpaqueLayers);

       这段代码用来计算当前正在处理的应用程序窗口的被覆盖区域,以及再进一步计算它的可见区域,主要考虑是否被上层的不透明区域覆盖了。


       变量aboveCoveredLayers用来描述当前正在处理的应用程序窗口的所有上层应用程序窗口所组成的可见区域,将这个区域与当前正在处理的应用程序窗口的可见区域visibleRegion相交,就可以得到当前正在处理的应用程序窗口的被覆盖区域coveredRegion,而将这个区域与当前正在处理的应用程序窗口的可见区域visibleRegion相或一下,就可以得到下一个应用程序窗口的所有上层应用程序窗口所组成的可见区域aboveCoveredLayers。

       变量aboveOpaqueLayers用来描述当前正在处理的应用程序窗口的所有上层应用程序窗口所组成的完全不透明区域,这个区域从当前正在处理的应用程序窗口的可见区域visibleRegion减去后,就可以得到当前正在处理的应用程序窗口的最终可见区域visibleRegion。

       我们继续往下阅读代码:

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
// compute this layer\'s dirty region
if (layer->contentDirty) {
// we need to invalidate the whole region
dirty = visibleRegion;
// as well, as the old visible region
dirty.orSelf(layer->visibleRegionScreen);
layer->contentDirty =  false ;
else {
/* compute the exposed region:
*   the exposed region consists of two components:
*   1) what\'s VISIBLE now and was COVERED before
*   2) what\'s EXPOSED now less what was EXPOSED before
*
* note that (1) is conservative, we start with the whole
* visible region but only keep what used to be covered by
* something -- which mean it may have been exposed.
*
* (2) handles areas that were not covered by anything but got
* exposed because of a resize.
*/
constRegion newExposed = visibleRegion - coveredRegion;
constRegion oldVisibleRegion = layer->visibleRegionScreen;
constRegion oldCoveredRegion = layer->coveredRegionScreen;
constRegion oldExposed = oldVisibleRegion - oldCoveredRegion;
dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
}
dirty.subtractSelf(aboveOpaqueLayers);
// accumulate to the screen dirty region
dirtyRegion.orSelf(dirty);

       这段代码用来计算屏幕的脏区域。我们首先解释一下屏幕的脏区域是如何计算的。将所有应用程序窗口的脏区域都组合起来,就可以得到屏幕的脏区域,这个脏区域就是需要重新执行渲染操作的。因此,为了得到屏幕的脏区域,我们要知道当前正在处理的应用程序窗口的脏区域,以及之前已经处理了的应用程序窗口脏区域组合。前者使用变量dirty来描述,而后者使用输出参数dirtyRegion来描述。从前面的调用过程可以知道,输出参数dirtyRegion指向的就正好是SurfaceFlinger类的成员变量mDirtyRegion,因此,当这一步执行完成之后,SurfaceFlinger类的成员变量mDirtyRegion就代表了SurfaceFlinger服务所要渲染的脏区域。


       我们首先来看当前正在处理的应用程序窗口的脏区域dirty是如何计算的。我们分两种情况来考虑。

       首先考虑当前正在处理的应用程序窗口上一次的状态还未来得及处理的情况,即它当前的内容是脏的。在这种情况下,LayerBase对象layer的成员变量contentDirty的值就会等于true。这时候我们就需要将该应用程序窗口的上一次可见区域,以及当前的可见区域合并起来,形成一个大的脏区域,这样就可以将两次渲染操作合并成一次来执行。当前正在处理的应用程序窗口的上一次可见区域保存在LayerBase对象layer的成员变量visibleRegionScreen中,而它前的可见区域保存在变量visibleRegion中。将这两者相或一下,就可以得到当前正在处理的应用程序窗口的脏区域dirty。

       接着考虑当前正在处理的应用程序窗口上一次的状态已经处理了的情况,即它当前的内容不是脏的,这意味着它所要显示的内容没有发生变化。在这种情况下,就不需要重新渲染所有的可见区域。那么那些区域是需要重新渲染的呢?第一部分区域是之前是被覆盖的,现在不被覆盖了,第二部分是由于窗口大小变化而引发的新增不被覆盖区域。接下来,我们就来看看这两部分区域是如何计算的。

       将一个应用程序窗口的当前可见区域减去被覆盖区域,就可以它的当前不被覆盖的区域newExposed,按照同样的方法,我们可以也可以得到它的上一次不被覆盖的区域oldExposed。注意,一个应用程序窗口的上一次可见区域和被覆盖区域分别保存与它相对应的一个LayerBase对象的成员变量visibleRegionScreen和coveredRegionScreen中。这样,将一个应用程序窗口的当前不被覆盖的区域newExposed减去它的上一次不被覆盖的区域oldExposed,就可以得到新增的不被覆盖区域,即可以得到第二部分需要重新渲染的区域。另一方面,将一个应用程序窗口的当前可见区域visibleRegion与它的上一次被覆盖区域oldCoveredRegion相交,就可以得到之前是被覆盖的而现在不被覆盖了的区域,即可以得到第一部分需要重新渲染的区域。将第一部分和第二部分需要重新渲染的区域组合起来,就可以得到当前正在处理的应用程序窗口的脏区域dirty。

      得到了当前正在处理的应用程序窗口的脏区域dirty,接下来的事情就好办了。首先从该脏区域dirty减去上层的完全不透明区域,因为后者的渲染不需要当前应用程序窗口来参与,接着最将得到的新的脏区域dirty累加到输出参数dirtyRegion中去,这样就可以得到目前为止,SurfaceFlinger服务需要渲染的脏区域。

     我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
// Update aboveOpaqueLayers for next (lower) layer
aboveOpaqueLayers.orSelf(opaqueRegion);
// Store the visible region is screen space
layer->setVisibleRegion(visibleRegion);
layer->setCoveredRegion(coveredRegion);
// If a secure layer is partially visible, lock-down the screen!
if (layer->isSecure() && !visibleRegion.isEmpty()) {
secureFrameBuffer =  true ;
}
}

      这段代码是前面的while循环的几行结束代码,主要用来做三件事情。


       第一件事情是计算到目前为止所得到的上层应用程序窗口的完全不透明区域,这是通过组合当前正在处理的应用程序窗口的完全不透明区域与位于它上面的的所有应用程序窗口的完全不透明区域aboveOpaqueLayers来得到的,并且最终结果保存在变量aboveOpaqueLayers中。

      第二件事情是调用LayerBase对象layer的成员函数setVisibleRegion和setCoveredRegion来保存当前正在处理的应用程序窗口的可见区域和被覆盖区域。

      第三件事情是判断当前正在处理的应用程序窗口的内容是否受安全保护的。如果是的话,并且它的可见区域不为空,那么就需要将变量secureFrameBuffer的值设置为true,以表示当前SurfaceFlinger服务不可以执行截屏功能。

      我们接着往下阅读最后一段代码:

1
2
3
4
5
6
// invalidate the areas where a layer was removed
dirtyRegion.orSelf(mDirtyRegionRemovedLayer);
mDirtyRegionRemovedLayer.clear();
mSecureFrameBuffer = secureFrameBuffer;
opaqueRegion = aboveOpaqueLayers;
}

      由于前面我们得到SurfaceFlinger服务需要重新渲染的脏区域dirtyRegion只考虑了那些新增或者本来已经存在的应用程序窗口的,而没有考虑那些已经被删除了的应用程序窗口。那些已经被删除了的应用程序窗口所占据的区域保存在SurfaceFlinger类的成员变量mDirtyRegionRemovedLayer中,因此,将它从输出参数dirtyRegion减去之后得到的才是SurfaceFlinger服务最终需要重新渲染的脏区域。


      此外,函数还将变量secureFrameBuffer的值保存在urfaceFlinger类的成员变量mSecureFrameBuffer中,以便SurfaceFlinger服务可以知道自己可以执行截屏功能。

      最后,函数还将前面所有的应用程序窗口组成的完全不透明区域aboveOpaqueLayers保存在输出参数opaqueRegion,以便可以返回给调用者使用。

      这一步执行完成之后,返回到前面的Step 1中,即SurfaceFlinger类的成员函数handlePageFlip中,接下来就会继续调用SurfaceFlinger类的另外一个成员函数unlockPageFlip来让各个应用程序窗口执行一些清理工作。

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

      Step 6. SurfaceFlinger.unlockPageFlip

1
2
3
4
5
6
voidSurfaceFlinger::unlockPageFlip(constLayerVector& currentLayers)
{
constGraphicPlane& plane(graphicPlane( 0 ));
constTransform& planeTransform(plane.transform());
size_t count = currentLayers.size();
sp<layerbase>  const * layers = currentLayers.array();     for (size_t i= 0 ; i<count ; i++) {        constsp<layerbase>& layer(layers[i]);        layer->unlockPageFlip(planeTransform, mDirtyRegion);    }}</layerbase></layerbase>

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


       前面提到,我们假设在SurfaceFlinger服务这一侧,每一个应用程序窗口都是使用一个Layer对象来描述,这些Layer对象都是从LayerBase继承下来的,因此它们可以保存在一个类型为LayerBase的向量中。

       从前面的调用过程可以知道,参数currentLayers里面保存的一系列LayerBase对象正是用来描述系统当前的各个应用程序窗口的,SurfaceFlinger类的成员函数unlockPageFlip依次调用这些LayerBase对象的成员函数unlockPageFlip来让它们有机会执行一些清理工作。由于我们假设这些LayerBase对象的实际类型为Layer,因此,前面调用的实际上就是Layer类的成员函数unlockPageFlip。接下来,我们就继续分析Layer类的成员函数unlockPageFlip的实现。

       Step 7. Layer.unlockPageFlip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
voidLayer::unlockPageFlip(
constTransform& planeTransform, Region& outDirtyRegion)
{
Region dirtyRegion(mPostedDirtyRegion);
if (!dirtyRegion.isEmpty()) {
mPostedDirtyRegion.clear();
// The dirty region is given in the layer\'s coordinate space
// transform the dirty region by the surface\'s transformation
// and the global transformation.
constLayer::State& s(drawingState());
constTransform tr(planeTransform * s.transform);
dirtyRegion = tr.transform(dirtyRegion);
// At this point, the dirty region is in screen space.
// Make sure it\'s constrained by the visible region (which
// is in screen space as well).
dirtyRegion.andSelf(visibleRegionScreen);
outDirtyRegion.orSelf(dirtyRegion);
}
if (visibleRegionScreen.isEmpty()) {
// an invisible layer should not hold a freeze-lock
// (because it may never be updated and therefore never release it)
mFreezeLock.clear();
}
}

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


      参数planeTransform用来描述系统显示屏的旋转方向,它是一个变换矩阵,而另外一个参数outDirtyRegion是一个输出参数,指向了SurfaceFlinger类的成员函数mDirtyRegion,即它描述的是SurfaceFlinger服务需要渲染的脏区域。

      Layer类的成员变量mPostedDirtyRegion用来描述当前正在处理的应用程序窗口的脏区域,即可能需要渲染的区域。从前面的Step 5可以知道,Layer类的成员变量visibleRegionScreen是从父类LayerBase继续下来的,用来描述当前正在处理的应用程序窗口的可见区域。这两个区域相关的那部分区域才是当前正在处理的应用程序窗口需要重新渲染的区域,因为一个区域如果是脏的,但是它同时也是不可见的,那么我们是不需要去渲染的。注意,Layer类的成员变量visibleRegionScreen的所描述的区域是相对于显示屏的,而Layer类的成员变量mPostedDirtyRegion所描述的区域是相对于当前正在处理的应用程序窗口的,因此,需要将它转换成相对于显示屏的区域之后,才能将它与前者执行一个相交操作,从而得到当前正在处理的应用程序窗口真正需要渲染的脏区域dirtyRegion。有了这个脏区域dirtyRegion之后,就可以将它组合到输出参数outDirtyRegion中去,以便可以得到SurfaceFlinger服务需要渲染的总脏区域。

      当Layer类的成员变量visibleRegionScreen所描述的区域为空时,就说明当前正在处理的应用程序窗口是不需要参与本次的渲染操作的,因此,这时候就要判断当前正在处理的应用程序窗口是否拥有一个用来冻结系统显示屏的锁。如果有的话,那么就要将这个锁释放掉,避免阻止SurfaceFlinger服务渲染其它应用程序的UI。释放当前正在处理的应用程序窗口所拥有的一个用来冻结系统显示屏的锁是通过调用Layer类的成员变量mFreezeLock所描述的一个FreezeLock对象的成员函数clear来完成的。

      至此,我们就分析完成各个应用程序窗口是如何设置它们当前所要渲染的图形缓冲区以及计算它们的可见区域的了,接下来我们继续分析当当前需要渲染的应用程序窗口只有一个,并且SurfaceFlinger服务在编译的时候指定了USE_COMPOSITION_BYPASS宏时,这个唯一的应用程序窗口是如何被渲染的,即分析SurfaceFlinger类的成员函数handleBypassLayer的实现。

     3. handleBypassLayer

     SurfaceFlinger类的成员函数handleBypassLayer跳过应用程序窗口的图形缓冲区合成阶段,它直接将系统中唯一的一个应用程序窗口的图形缓冲区渲染到硬件帧缓冲区中去,它的执行过程如图9所示:

1351367276_9438.jpg

图9 SurfaceFlinger服务直接渲染应用程序窗口的图形缓冲区的过程

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

     Step 1. SurfaceFlinger.handleBypassLayer

1
2
3
bool SurfaceFlinger::handleBypassLayer()
{
sp<layer> bypassLayer(mBypassLayer.promote());     if (bypassLayer !=  0 ) {        sp<graphicbuffer> buffer(bypassLayer->getBypassBuffer());         if (buffer!= 0 && (buffer->usage & GRALLOC_USAGE_HW_FB)) {            constDisplayHardware& hw(graphicPlane( 0 ).displayHardware());            hw.postBypassBuffer(buffer->handle);            returntrue;        }    }    returnfalse;}</graphicbuffer></layer>

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


      从前面第2部分的内容的Step 1可以知道,SurfaceFlinger类的成员变量mBypassLayer指向了系统中唯一需要渲染的应用程序窗口,当它的值不等于0的时候,函数接下来就会检查它当前需要渲染的图形缓冲区buffer是否是直接在硬件帧缓冲区中分配的,即图形缓冲区buffer的成员变量usage的GRALLOC_USAGE_HW_FB位是否等于1。如果是直接在硬件帧缓冲区中分配的话,那么函数最后就会先获得用来描述系统主显示屏的一个DisplayHardware对象hw的成员函数postBypassBuffer来直接渲染图形缓冲区buffer。

     在前面第2部分的内容的Step 1中提到,用来描述系统中唯一需要渲染的应用程序窗口的一个Layer对象的成员变量mBypassState的值会被设置为true。这样做的目的是为了让SurfaceFlinger服务以后在为这个应用程序窗口分配图形缓冲区时,都直接在硬件帧缓冲区中分配,这一点可以参考前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文,这样SurfaceFlinger服务以后就可以直接将该图形缓冲区渲染到硬件帧缓冲区上。与那些在匿名共享内存中分配的图形缓冲区的渲染过程相比,直接在硬件帧缓冲区中分配的图形缓冲区的渲染过程要高效得多,因从就可以提高系统UI的显示性能。

     接下来,我们就继续分析DisplayHardware类的成员函数postBypassBuffer的实现,以便可以了解系统中唯一需要渲染的应用程序窗口的图形缓冲区是如何被直接渲染到硬件帧缓冲区中去的。

     Step 2. DisplayHardware.postBypassBuffer

1
2
3
4
5
status_t DisplayHardware::postBypassBuffer(constnative_handle_t* handle)  const
{
framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice();
returnfbDev->post(fbDev, handle);
}

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


     从前面Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析一文可以知道,DisplayHardware类的成员变量mNativeWindow指向的是一个FramebufferNativeWindow对象,调用这个FramebufferNativeWindow对象的成员函数getDevice就可以获得它里面的一个framebuffer_device_t对象fbDev。这个framebuffer_device_t对象fbDev是在HAL层的模块Gralloc中打开的,通过调用它的成员函数post就可以将指定的图形缓冲区渲染到硬件帧缓冲区中去。

     接下来,我们就继续分析framebuffer_device_t类的成员函数post的实现,以便可以了解SurfaceFlinger是如何将一个图形缓冲区渲染到硬件帧缓冲区中去的。

     Step 3. framebuffer_device_t.post

     从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,framebuffer_device_t类的成员函数post指向了HAL层的Gralloc模块的函数fb_post中,后者定义在文件hardware/libhardware/modules/gralloc/framebuffer.cpp。

     HAL层的Gralloc模块的函数fb_post的作用就是将一个指定的图形缓冲区的内容渲染到硬件帧缓冲区中去。它会分两种情况来考虑。第一种情况是指定的图形缓冲区是直接在硬件帧缓冲区中分配的,这时候该函数就使用IO控制命令FBIOPUT_VSCREENINFO来直接在硬件帧缓冲区中渲染该图形缓冲区的内容。第二种情况是指定的图形缓冲区是直接在匿名共享内存中分配,这时候该函数就会调用函数memcpy来将图形缓冲区的内容从匿名共享内存拷贝硬件帧缓冲区中去。在我们这个场景中,指定要渲染的图形缓冲区是直接在硬件帧缓冲区中分配的,因此,它最终就会通过IO控制命令FBIOPUT_VSCREENINFO渲染到硬件帧缓冲区中去。这个过程可以参考Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文,这里不再详述。

      至此,我们就分析完成系统中唯一一个需要渲染的应用程序窗口的图形缓冲区的渲染过程了,接下来我们继续分析系统中存在多个需要渲染的应用程序窗口时,SurfaceFlinger服务是如何渲染它们的图形缓冲区的,即分析SurfaceFlinger类的成员函数handleRepaint的实现。

      4. handleRepaint

      SurfaceFlinger类的成员函数handleRepaint是用来合成系统中各个应用程序窗口的图形缓冲区的,以便可以将它们的内容一起渲染到硬件帧缓冲区中去,它的执行过程如图10所示:

1351442462_7660.jpg

图10 SurfaceFlinger服务合成应用程序窗口的图形缓冲区的过程

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

       Step 1. SurfaceFlinger.handleRepaint

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
49
50
voidSurfaceFlinger::handleRepaint()
{
// compute the invalid region
mInvalidRegion.orSelf(mDirtyRegion);
if (mInvalidRegion.isEmpty()) {
// nothing to do
return ;
}
if (UNLIKELY(mDebugRegion)) {
debugFlashRegions();
}
// set the frame buffer
constDisplayHardware& hw(graphicPlane( 0 ).displayHardware());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
uint32_t flags = hw.getFlags();
if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
(flags & DisplayHardware::BUFFER_PRESERVED))
{
// we can redraw only what\'s dirty, but since SWAP_RECTANGLE only
// takes a rectangle, we must make sure to update that whole
// rectangle in that case
if (flags & DisplayHardware::SWAP_RECTANGLE) {
// TODO: we really should be able to pass a region to
// SWAP_RECTANGLE so that we don\'t have to redraw all this.
mDirtyRegion.set(mInvalidRegion.bounds());
else {
// in the BUFFER_PRESERVED case, obviously, we can update only
// what\'s needed and nothing more.
// NOTE: this is NOT a common case, as preserving the backbuffer
// is costly and usually involves copying the whole update back.
}
else {
if (flags & DisplayHardware::PARTIAL_UPDATES) {
// We need to redraw the rectangle that will be updated
// (pushed to the framebuffer).
// This is needed because PARTIAL_UPDATES only takes one
// rectangle instead of a region (see DisplayHardware::flip())
mDirtyRegion.set(mInvalidRegion.bounds());
else {
// we need to redraw everything (the whole screen)
mDirtyRegion.set(hw.bounds());
mInvalidRegion = mDirtyRegion;
}
}
// compose all surfaces
composeSurfaces(mDirtyRegion);
// clear the dirty regions
mDirtyRegion.clear();
}

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


      SurfaceFlinger类的成员变量mInvalidRegion用来描述SurfaceFlinger服务需要渲染到硬件帧缓冲区中去的脏区域的。前面提到,SurfaceFlinger类的成员变量mDirtyRegion也是用来描述SurfaceFlinger服务需要渲染的脏区域的,不过,它的作用是用来合成系统中各个应用程序窗口的图形缓冲区的,也就是说,当系统中各个应用程序窗口的图形缓冲区被合成之后,这个成员变量所描述的区域就会被清空,而成员变量mInvalidRegion会一直等到它的内容被渲染到硬件帧缓冲区中去之后,才会被清空。这样就可能会出现这种情况:上一次合成的图形缓冲区还未被渲染到硬件帧缓冲区中去,SurfaceFlinger服务又会执行新一轮的系统中各个应用程序窗口的图形缓冲区的合并操作。在这种情况下,SurfaceFlinger服务就需要将前面两次需要渲染到硬件帧缓冲区中去的区域合并在一起,以便可以正确地反映被刷新的UI。因此,函数在开头的地方,就会先SurfaceFlinger类的成员变量mDirtyRegion所描述的区域组合到成员变量mInvalidRegion所描述的区域中去。

      函数接下来调用用来描述系统主显示屏的一个DisplayHardware对象hw的成员函数getFlags来获得系统所支持的渲染方式,并且保存在一个uint32_t变量flags中。接下来,我们就分四种情况来讨论系统所支持的渲染方式:

     1. 变量flags的DisplayHardware::PARTIAL_UPDATES位等于1。在这种情况下,系统在硬件上直接支持部分区域更新功能,不过,这个部分被更新的区域必须要是一个矩形区域。

     2. 变量flags的DisplayHardware::SWAP_RECTANGLE位等于1。在这种情况下,系统在软件上支持部分区域更新功能,同样,这个部分被更新的区域必须要是一个矩形区域。

     3. 变量flags的DisplayHardware::BUFFER_PRESERVED位等于1。在这种情况下,系统支持不规则的部分区域更新功能。所谓不规则,就是这个被更新的区域不必是一个矩形区域。

     4. 变量flags的值等于0。在这种情况下,系统不支持部分更新区域,这时候就需要更新整个屏幕的内容。

     在第1种和第2种情况中,由于被更新的区域都必须是一个矩形区域,因此,函数就需要将SurfaceFlinger类的成员变量mDirtyRegion所描述的一个区域设置为包含了所有脏区域的一个最小矩形区域。在第4种情况中,由于需要更新的是整个屏幕的内容,因此,函数就需要将SurfaceFlinger类的成员变量mDirtyRegion所描述的一个区域设置为等于屏幕大小的一个矩形区域。在第3种情况中,就不需要调用被更新的区域。对于第1种、第2种和第3种渲染方式的更多描述,可以参考前面Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析一文。

     得到了最终要合成的脏区域mDirtyRegion之后,SurfaceFlinger类的成员函数handleRepaint最后就调用另外一个成员函数composeSurfaces来合成系统中各个应用程序窗口的图形缓冲区,并且在合成操作完成之后,将脏区域mDirtyRegion设置为一个空区域。

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

     Step 2. SurfaceFlinger.composeSurfaces

1
2
3
4
5
6
7
8
voidSurfaceFlinger::composeSurfaces(constRegion& dirty)
{
if (UNLIKELY(!mWormholeRegion.isEmpty())) {
// should never happen unless the window manager has a bug
// draw something...
drawWormhole();
}
constVector< sp<layerbase> >& layers(mVisibleLayersSortedByZ);    constsize_t count = layers.size();     for (size_t i= 0 ; i<count ; ++i) {        constsp<layerbase>& layer(layers[i]);        constRegion clip(dirty.intersect(layer->visibleRegionScreen));         if (!clip.isEmpty()) {            layer->draw(clip);        }    }}</layerbase></layerbase>

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

       参数dirty用来描述即将要被渲染的脏区域,它是从前面的第1步传进来的,即为SurfaceFlinger类的成员变量mDirtyRegion所描述的区域。

       SurfaceFlinger类的成员变量mVisibleLayersSortedByZ保存了系统当前所有的可见应用程序窗口,这些应用程序窗口是前面第2部分内容的Step 1中计算得到的,而且都是需要渲染的。 SurfaceFlinger类的成员函数composeSurfaces依次检查这些应用程序窗口的可见区域是否与即将要渲染的的脏区域有交集。如果有的话,即变量clip所描述的区域不为空,那么接下来SurfaceFlinger类的成员函数composeSurfaces就会分别调用与这些应用程序窗口对应的一个LayerBase对象的成员函数draw来将它们需要渲染的图形缓冲区合成到系统的主显示屏来。

      接下来,我们就继续分析LayerBase类的成员函数draw的实现,以便了解SurfaceFlinger服务合成各个应用程序窗口的图形缓冲区的过程。

      Step 3. LayerBase.draw

1
2
3
4
5
6
voidLayerBase::draw(constRegion& clip)  const
{
// reset GL state
glEnable(GL_SCISSOR_TEST);
onDraw(clip);
}

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


       LayerBase类的成员函数draw的实现很简单,它只是调用了另外一个成员函数onDraw来通知各个应用程序窗口重新绘制参数clip所描述的一个区域。LayerBase类的成员函数onDraw是一个虚函数,这是由其子类来重写的。前面我们假设系统中的所有应用程序窗口都是使用一个Layer对象来描述的,而Layer类是从LayerBase类继承下来的,并且重写了其成员函数onDraw。因此,接下来我们就继续分析Layer类的成员函数onDraw的实现。

       Step 4. Layer.onDraw

       这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中,用来绘制一个应用程序窗口的指定区域,我们分段来阅读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
voidLayer::onDraw(constRegion& clip)  const
{
Texture tex(mBufferManager.getActiveTexture());
if (tex.name == -1LU) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
// SurfaceView because the WindowManager can\'t know when the client
// has drawn the first time.
// If there is nothing under us, we paint the screen in black, otherwise
// we just skip this update.
// figure out if there is something below us
Region under;
constSurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ);
constsize_t count = drawingLayers.size();
for (size_t i= 0 ; i<count ;= "" ++i)= "" {= "" const = "" sp<layerbase= "" >& layer(drawingLayers[i]);             if (layer.get() == static_cast<layerbase>( this ))                 break ;            under.orSelf(layer->visibleRegionScreen);        }         // if not everything below us is covered, we plug the holes!        Region holes(clip.subtract(under));        if (!holes.isEmpty()) {            clearWithOpenGL(holes, 0, 0, 0, 1);        }        return;    }</layerbase></count>

       这段代码主要是用来处应用程序窗口的纹理尚未创建好的情况。


       在纹理未创建好的情况下,一个应用程序窗口是不应该被渲染的。这时候函数首先将位于当前正在处理的应用程序窗口下面的所有其它应用程序窗口的可见区域组合起来,并且保存在变量under所描述的区域中。由于这时候当前正在处理的应用程序窗口不会被绘制,因此,如果变量under所描述的区域小于参数clip所描述的区域,即变量holes所描述的区域不为空,那么SurfaceFlinger服务所要求缓制的区域clip就会留下一个洞。这个洞会被绘制成黑色,这是通过调用函数clearWithOpenGL来实现的。绘制完成之后,函数就可以直接返回了。

       在前面第2部分的Step 4中提到,Layer类的成员变量mBufferManager描述的是一个BufferManager对象,通过调用它的成员函数initEglImage或者loadTexture就可以根据一个应用程序窗口的当前需要渲染的图形缓冲区来创建出一个纹理对象。这个纹理对象就保存在BufferManager类的成员变量mFailoverTexture或者另外一个成员变量mBufferData所描述的一个BufferData数组中,取决于系统是否支持在硬件上直接创建纹理对象。这个纹理对象是使用一个Texture对象来描述的,并且可以通过调用Layer类的成员变量mBufferManager描述的是一个BufferManager对象的成员函数getActiveTexture来获得。如果获得的Texture对象的名称name等于-1,那么就说明当前正在处理的应用程序窗口尚未创建好需要渲染的纹理。

       我们继续往下阅读代码:

1
2
#ifdef USE_COMPOSITION_BYPASS
sp<graphicbuffer> buffer(mBufferManager.getActiveBuffer());     if ((buffer != NULL) && (buffer->transform)) {         // Here we have a \"bypass\" buffer, but we need to composite it        // most likely because it\'s not fullscreen anymore.        // Since the buffer may have a transformation applied by the client        // we need to inverse this transformation here.        // calculate the inverse of the buffer transform        const uint32_t mask = HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;        const uint32_t bufferTransformInverse = buffer->transform ^ mask;        // To accomplish the inverse transform, we use \"mBufferTransform\"        // which is not used by Layer.cpp        const_cast<layer*>(this)->mBufferTransform = bufferTransformInverse;        drawWithOpenGL(clip, tex);        // reset to \"no transfrom\"        const_cast<layer*>(this)->mBufferTransform = 0;        return;    }#endif</layer*></layer*></graphicbuffer>

      这段代码用来检查当前正在处理的应用程序窗口的图形缓冲区是否是一个可以跳过合成阶段的图形缓冲区。本来这种图形缓冲区是可以直接渲染到硬件帧缓冲区中去的,但是由于它不是全屏显示的,因此就需要与其它应用程序窗口的图形缓冲区进行合并操作。如果这个图形缓冲区之前曾经被旋转过,例如,被水平翻转或者垂直翻转过,那么在对它进行合并之前,还需要将它的旋转方向恢复回来。


      当用来描述一个图形缓冲区的一个GraphicBuffer对象的成员变量transform的值不等于0时,那么就说明这个图形缓冲区是被旋转过的,这时候函数就会对这个成员变量的值的HAL_TRANSFORM_FLIP_V位或者HAL_TRANSFORM_FLIP_H位进行取反,目的就是为了恢复它之前的旋转方向。反转后得到的方向就保存在Layer类的成员变量mBufferTransform中。Layer类的成员变量mBufferTransform是从父类LayerBase继承下来的,接下来在调用从父类LayerBase继承下来的成员函数drawWithOpenGL来绘制当前正在处理的应用程序窗口时,就需要使用到它来设置纹理的旋转方向。在后面的Step 5中,我们再详细分析LayerBase类的成员函数drawWithOpenGL的实现。

      我们继续往下阅读最后一行代码:

1
2
drawWithOpenGL(clip, tex);
}

       如果当前正在处理的应用程序窗口的图形缓冲区没有被旋转过,或者这个图形缓冲区本来就需要进行合并的,那么Layer类的成员函数onDraw最后就会调用从父类LayerBase继承下来的成员函数drawWithOpenGL来将这个图形缓冲区的内容绘制在系统的主显示屏的指定区域上来。这个图形缓冲区的内容是使用纹理象tex来描述的,而指定的主显示屏区域是由参数clip来描述的。

       接下来,我们就继续分析LayerBase类的成员函数drawWithOpenGL的实现,以便可以了解一个应用程序窗口的绘制过程,即它的图形缓冲区被合成到系统主显示屏的过程。

       Step 5. LayerBase.drawWithOpenGL

       这个函数定义在文件frameworks/base/services/surfaceflinger/LayerBase.cpp中,它通过OpenGL提供的接口来绘制一个应用程序窗口的指定区域,我们分段来阅读:

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
voidLayerBase::drawWithOpenGL(constRegion& clip, constTexture& texture)  const
{
constDisplayHardware& hw(graphicPlane( 0 ).displayHardware());
constuint32_t fbHeight = hw.getHeight();
constState& s(drawingState());
// bind our texture
TextureManager::activateTexture(texture, needsFiltering());
uint32_t width  = texture.width;
uint32_t height = texture.height;
GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
if (UNLIKELY(s.alpha <  0xFF )) {
constGLfloat alpha = s.alpha * ( 1 .0f/ 255 .0f);
if (mPremultipliedAlpha) {
glColor4f(alpha, alpha, alpha, alpha);
else {
glColor4f( 1 1 1 , alpha);
}
glEnable(GL_BLEND);
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
else {
glColor4f( 1 1 1 1 );
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
if (needsBlending()) {
glEnable(GL_BLEND);
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
else {
glDisable(GL_BLEND);
}
}

       参数clip用来描述要绘制的区域,而参数texture用来描述要绘制的纹理。


       这段代码首先得到主显示屏的高度fbHeight、要绘制的纹理texture的宽度width和高度height,以及用来描述当前正在处理的应用程序窗口状态的一个State对象s,接下来就是调用函数再设置纹理texture的绘制模式,即是否需要以混合模式来绘制,这是通过调用函数glEnable(GL_BLEND)来实现的。在需要混合模式来绘制纹理texture的情况下,还需要调用函数glBlendFunc来指定混合函数。

       在以下两种情况下,纹理texture需要以混合模式来绘制:

       1. 当前正在处理的应用程序窗口的Alpha通道的值小于0xFF,即State对象s的成员变量alpha的值小于0xFF,这表明该窗口的背景是半透明的。

       2. 当前正在处理的应用程序窗口的像素格式是半透明的,这是通过调用LayerBase类的成员函数needsBlending来判断的。Layer类重写了父类LayerBase的成员函数needsBlending。当一个Layer对象所描述的应用程序窗口的像素格式是半透明的时候,它就会将它的成员变量mNeedsBlending的值设置为true。这样,我们就可以调用Layer类的成员函数needsBlending来检查它的成员变量mNeedsBlending是否为true来判断一个应用程序窗口的像素格式是否是半透明的。这一点可以参考前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文的Step 6。

      我们继续往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
*  compute texture coordinates
*  here, we handle NPOT, cropping and buffer transformations
*/
GLfloat cl, ct, cr, cb;
if (!mBufferCrop.isEmpty()) {
// source is cropped
constGLfloat us = (texture.NPOTAdjust ? texture.wScale :  1 .0f) / width;
constGLfloat vs = (texture.NPOTAdjust ? texture.hScale :  1 .0f) / height;
cl = mBufferCrop.left   * us;
ct = mBufferCrop.top    * vs;
cr = mBufferCrop.right  * us;
cb = mBufferCrop.bottom * vs;
else {
cl =  0 ;
ct =  0 ;
cr = (texture.NPOTAdjust ? texture.wScale :  1 .0f);
cb = (texture.NPOTAdjust ? texture.hScale :  1 .0f);
}

       这段代码用来计算纹理坐标,并且保存在变量cl、ct、cr和cb中,用来描述纹理texture的四个角的坐标。


       从前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文可以知道,如果Android应用程序设置了一个窗口的纹理坐标,那么SurfaceFlinger服务就会通过调用LayerBase类的成员函数setBufferCrop来保存在成员变量mBufferCrop中。另一方面,如果Android应用程序没有指定一个窗口的纹理坐标,那么这个窗口的纹理坐标的默认值就使用要绘制的纹理的四个角的坐标来描述。注意,在计算纹理坐标的时候,还要考虑纹理的大小,以及纹理本身所设置的缩放因子,以便可以正确地将纹理绘制在应用程序窗口中。

       我们继续往下阅读代码:

1
2
3
4
5
6
/*
* For the buffer transformation, we apply the rotation last.
* Since we\'re transforming the texture-coordinates, we need
* to apply the inverse of the buffer transformation:
*   inverse( FLIP_V -> FLIP_H -> ROT_90 )
*   <=> inverse( ROT_90 * FLIP_H * FLIP_V ) *    =  inverse(FLIP_V) * inverse(FLIP_H) * inverse(ROT_90) *    =  FLIP_V * FLIP_H * ROT_270 *   <=> ROT_270 -> FLIP_H -> FLIP_V * * The rotation is performed first, in the texture coordinate space. * */ struct TexCoords {    GLfloat u;    GLfloat v;}; enum {     // name of the corners in the texture map    LB = 0, // left-bottom    LT = 1, // left-top    RT = 2, // right-top    RB = 3  // right-bottom};// vertices in screen spaceint vLT = LB;int vLB = LT;int vRB = RT;int vRT = RB;// the texture\'s source is rotateduint32_t transform = mBufferTransform;if (transform & HAL_TRANSFORM_ROT_90) {    vLT = RB;    vLB = LB;    vRB = LT;    vRT = RT;}if (transform & HAL_TRANSFORM_FLIP_V) {    swap(vLT, vLB);    swap(vRT, vRB);}if (transform & HAL_TRANSFORM_FLIP_H) {    swap(vLT, vRT);    swap(vLB, vRB);}TexCoords texCoords[4];texCoords[vLT].u = cl;texCoords[vLT].v = ct;texCoords[vLB].u = cl;texCoords[vLB].v = cb;texCoords[vRB].u = cr;texCoords[vRB].v = cb;texCoords[vRT].u = cr;texCoords[vRT].v = ct;<!--=--><!--=-->

       这段代码主要根据当前正在处理的应用程序窗口的旋转方向来调整前面所计算得到的纹理坐标。


       从前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文可以知道,如果Android应用程序设置了一个窗口的旋转方向时,那么SurfaceFlinger服务就会通过调用LayerBase类的成员函数setBufferTransform来保存在成员变量mBufferTransform中,因此,这段代码就可以根据这个成员变量的值来相应地调用前面所计算得到的纹理坐标。

       我们继续往下阅读代码:

1
2
3
4
5
if (needsDithering()) {
glEnable(GL_DITHER);
else {
glDisable(GL_DITHER);
}

       这段代码用来检查是否需要以抖动的方式来绘制纹理,如果需要的话,就调用函数glEnable(GL_DITHER)来启动抖动功能,否则的话,就调用函数glDisable(GL_DITHER)来关闭抖动功能。


       从前面Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析一文可以知道,如果一个Android应用程序窗口的像素格式的红色通道的位数大于系统主显示屏的像素格式的红色通道的位数时,SurfaceFlinger服务就会将用来描述该Android应用程序窗口的一个Layer对象的成员变量mNeedsDithering的值设置为true。Layer类重写了父类LayerBase的成员函数needsDithering,它通过检查其成员变量mNeedsDithering的值是否等于true来告诉这段代码用来检查是否需要以抖动的方式来绘制纹理。

       我们继续往下阅读最后一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer( 2 , GL_FLOAT,  0 , mVertices);
glTexCoordPointer( 2 , GL_FLOAT,  0 , texCoords);
Region::const_iterator it = clip.begin();
Region::const_iterator constend = clip.end();
while (it != end) {
constRect& r = *it++;
constGLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN,  0 4 );
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

      这段代码首先调用函数glEnableClientState(GL_TEXTURE_COORD_ARRAY)来指定使用顶点数组的方式绘制当前正在处理的应用程序窗口,接着又分别调用函数glVertexPointer和glTexCoordPointer来指定要绘制的顶点数组以及纹理坐标。设置好要绘制的顶点数组以及纹理坐标之后,最后就要设置裁剪区域,以便可以调用函数glDrawArrays来绘制前面指定的顶点数组以及纹理。


       前面提到,参数clip用来描述要绘制的区域,而要绘制的区域即为当前需要指定的裁剪区域。由于参数clip所描述的区域是可以由一系列的矩形区域来组成的,因此,这段代码就通过一个while循环来遍历里面的每一个矩形区域,并且调用函数glScissor来分别将它们设置为裁剪区域。

       至此,我们就分析完成系统中各个应用程序窗口的图形缓冲区的合成过程了,这个过程其实就是分别根据各个应用程序窗口的图形缓冲区来创建一个纹理对象,并且结合各个应用程序窗口的UI元数据来将该纹理对象绘制在系统主显示屏的指定区域中,而这个指定的区域即为系统的脏区域。

       接下来,我们就继续分析系统中各个应用程序窗口将自己的图形缓冲区合成到系统的主显示屏之后,SurfaceFlinger服务是如何将系统的主显示屏渲染到硬件帧缓冲区中去的,即分析SurfaceFlinger类的成员函数postFramebuffer的实现。

       5. postFramebuffer

       SurfaceFlinger类的成员函数postFramebuffer用来将系统的主显示屏的内容渲染到硬件帧缓冲区中去,它的执行过程如图11所示:

1351701271_4273.jpg

图11 SurfaceFlinger服务渲染系统主显示屏的内容到硬件帧缓冲区的过程

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

       Step 1. SurfaceFlinger.postFramebuffer

1
2
3
4
5
6
7
8
9
10
11
12
voidSurfaceFlinger::postFramebuffer()
{
if (!mInvalidRegion.isEmpty()) {
constDisplayHardware& hw(graphicPlane( 0 ).displayHardware());
constnsecs_t now = systemTime();
mDebugInSwapBuffers = now;
hw.flip(mInvalidRegion);
mLastSwapBufferTime = systemTime() - now;
mDebugInSwapBuffers =  0 ;
mInvalidRegion.clear();
}
}

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


       在前面第4部分内容的Step 1中提到,SurfaceFlinger类的成员变量mInvalidRegion用来描述系统主显示屏的脏区域,即SurfaceFlinger服务当前需要渲染的区域。函数首先得到用来描述系统主显示屏的一个DisplayHardware对象hw,接着再调用这个DisplayHardware对象hw的成员函数flip来渲染这个脏区域。

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

       Step 2.  DisplayHardware.flip

       这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中,在前面Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析一文中,我们已经分析过它的实现了。这个函数会检查系统是否支持部分更新功能。如果支持的话,那么就先设置要更新的区域,否则的话,就直接调用函数eglSwapBuffers来将前面已经合成好的了图形缓冲区渲染到硬件帧缓冲区去。

       从前面Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析一文可以知道,调用函数eglSwapBuffers在渲染图形缓冲区的时候,会导致FramebufferNativeWindow类的成员函数queueBuffer被调用,后者会通过HAL层的Gralloc模块来执行渲染硬件帧缓冲区的操作。

      Step 3. FramebufferNativeWindow.queueBuffer

      这个函数定义在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中,同样,在前面Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析一文中,我们已经分析过它的实现了。这个函数主要就是通过HAL层的Gralloc模块中的framebuffer_device_t设备的成员函数post来执行渲染硬件帧缓冲区的操作。

      Step 4. framebuffer_device_t.post

      这个函数指向定义在HAL层的Gralloc模块中的函数fb_post,后者定义在文件hardware/libhardware/modules/gralloc/framebuffer.cpp,在前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文中,我们已经分析过这个函数的实现了。由于要渲染的图形缓冲区是用于渲染系统主显示屏的,因此,它是直接在硬件帧缓冲区上分配的,这时候函数fb_post就会通过IO控制命令FBIOPUT_VSCREENINFO来通知位内核空间的fb驱动来将系统主显示屏的UI绘制出来。

      至此,SurfaceFlinger服务渲染系统主显示屏的内容到硬件帧缓冲区的过程就分析完成了,整个SurfaceFlinger服务渲染应用程序UI的过程也分析完成了。

      这样,我们就通过Android应用程序与SurfaceFlinger服务的关系概述和学习计划Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划这两个系列的文章系统地分析了Android系统的SurfaceFlinger服务的实现,为后面我们后面进一步分析Android系统的UI架构打下坚实的基础!





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/1242720,如需转载请自行联系原作者
目录
相关文章
|
16天前
|
消息中间件 安全 数据处理
Android为什么不能在子线程更新UI
Android为什么不能在子线程更新UI
23 0
|
23天前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
18 1
|
2月前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
33 4
|
3月前
|
监控 Android开发 C语言
深度解读Android崩溃日志案例分析2:tombstone日志
深度解读Android崩溃日志案例分析2:tombstone日志
81 0
|
1月前
|
XML API Android开发
【Android 从入门到出门】第三章:使用Hilt处理Jetpack Compose UI状态
【Android 从入门到出门】第三章:使用Hilt处理Jetpack Compose UI状态
26 4
|
1月前
|
存储 XML 编译器
【Android 从入门到出门】第二章:使用声明式UI创建屏幕并探索组合原则
【Android 从入门到出门】第二章:使用声明式UI创建屏幕并探索组合原则
47 3
|
2月前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
24 4
|
2月前
|
安全 搜索推荐 Android开发
Android 与 iOS 的比较分析
【2月更文挑战第5天】 Android 和 iOS 是目前市场上两种最流行的移动操作系统,它们都拥有自己的特点和优势。本文将会分别从操作系统设计、应用生态、安全性等方面对这两种操作系统进行比较和分析,希望能够帮助读者更好地选择适合自己的移动设备。
|
3月前
|
安全 算法 JavaScript
安卓逆向 -- 关键代码定位与分析技术
安卓逆向 -- 关键代码定位与分析技术
39 0
|
1月前
|
前端开发 搜索推荐 开发者
SAP UI5 sap.m.Column 控件的 minScreenWidth 属性介绍
SAP UI5 sap.m.Column 控件的 minScreenWidth 属性介绍
27 0