首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > Android >

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

2012-11-22 
Android系统Surface机制的SurfaceFlinger服务渲染应用程序UI的过程分析在前面的一系列文章中,我们学习了An

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

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

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

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

图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所示。

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

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

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

         Step 1. SurfaceFlinger.handleTransaction


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

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

        Step 1. SurfaceFlinger.handlePageFlip


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

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

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

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

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

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

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

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

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

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

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

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

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

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

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

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

Android系统Surface机制的SurfaceFlinger服务点染应用程序UI的过程分析

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

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

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

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

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

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

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

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

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

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


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

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

      Step 1. SurfaceFlinger.handleBypassLayer


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

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

        Step 1. SurfaceFlinger.handleRepaint


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

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

        Step 1. SurfaceFlinger.postFramebuffer

void SurfaceFlinger::postFramebuffer(){    if (!mInvalidRegion.isEmpty()) {        const DisplayHardware& hw(graphicPlane(0).displayHardware());        const nsecs_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架构打下坚实的基础!


6楼wind_hzx3天前 21:56
你好,博主,我有个问题,希望能得到你的指导……n这是关于屏幕旋转的问题,比如我屏幕从竖屏切为横屏。n因为一个layer包含了许多的图元(比如图片等),当屏幕旋转时:n1、java层会首先做一个旋转,去操作矩阵(我想问一下这个操作是对什么的旋转进行操作,是对图元还是整个layer?)。n2、接着会在SF进行各个图层之间的裁剪操作。n3、最后如果是GPU加速的话,会进行各个图层之间的合成,最后写到framebuffer中。(在合成的时候貌似也会进行旋转操作,请问一下这个是对什么进行操作呢?和第一步的操作有什么区别,还有旋转操作是将旋转完后的数据写入到相对应的layer对应的内存吗?)n以上是我的一些困惑,希望博主能抽空帮忙解惑,谢谢!
Re: Luoshengyang3天前 22:11
回复wind_hzxnJava层的UI操作我还没去仔细研究,不过我想应该是这样的。每个Layer描述的是一个窗口,窗口里面会有很多图元。n对于SurfaceFlinger来说,它关心的是整个屏幕的内容,而窗口本身关心的是它内部的图元。n屏幕的内容是从哪里来的呢?就是从系统中的各个窗口组合而来的了。n窗口的内容又是从哪里来的呢?就是来自它内部的图元了。n这样SurfaceFlinger服务就会要求各个窗口先处理好自己的图元,处理完成之后,再把一个总的图形缓冲区交给SurfaceFlinger服务处理。n窗口是怎么处理自己的图元的呢?这就是通过skia这样的图形库来处理的了。通过skia这样的图形库,就可以对图元进行旋转等操作。注意,这时候对图元的操作是在窗口本身的坐标空间来进行的。n窗口处理完成自己的图元之后,就会得到一个总的图形缓冲区,交给SurfaceFlinger服务处理。由窗口也会有自己的旋转方向,这时相对屏幕而言的。再者,屏幕也有旋转方向。因此,SurfaceFlinger服务在合成各个窗口的图形缓冲区时,就要考虑窗口以及屏幕的旋转方向。这一步的合成操作是通过OpenGL ES库来完成的。n各个窗口的图形缓冲区合成之后,SurfaceFlinger服务就可以将它们合成到硬件帧缓冲区中去了。n大概原理就是这样,有没有硬件加速的区别就在于一些图形操作是GPU操作,还是在CPU操作,流程都是一样的。
Re: wind_hzx3天前 22:36
回复Luoshengyangn哦,原来如此,这样就好理解了。还有个想问一下,就是对于屏幕内容的旋转,你觉得是将旋转后的每个像素点信息写到内存中呢,还是直接在原来内存上加上一个偏移的值而已,这样读取内存的时候就直接加上偏移值就行?
Re: Luoshengyang昨天 10:09
回复wind_hzxn设置旋转方向不需要你自己去改变像素点的位置,你只要设置好纹理坐标就行了。因为图形缓冲区的内容就是用来创建纹理的,然后在设置纹理坐标的时候,就会考虑旋转方向。这个你需要了解一下OpenGL库,以及一些图形学的知识。
Re: wind_hzx昨天 10:27
回复Luoshengyangn看来我得多补补基础咯,谢谢你哈!
5楼zhang_heaven4天前 20:54
感谢楼主!
4楼shweli4天前 14:38
bucuo
3楼careland_hqh4天前 22:41
老罗辛苦了,这么晚还在码字,敬礼!
2楼wildwild15天前 13:57
咦,是c++的……
Re: Luoshengyang5天前 21:28
回复wildwild1n很快就会分析到Java接口的了。
1楼ronnie1635天前 09:04
很好很好

热点排行