Android系统Surface机制的SurfaceFlinger服务渲染应用程序UI的过程分析
在前面的一系列文章中,我们学习了Android应用程序与SurfaceFlinger服务的关系,以及SurfaceFlinger服务的启动过程、初始化硬件帧缓冲区的过程、线程模型。SurfaceFlinger服务所做的一切都是为了给Android应用程序提服务的,即为Android应用程序渲染它们的UI。在本文中,我们就详细分析SurfaceFlinger服务渲染Android应用程序UI的过程。
从前面Android系统Surface制的SurfaceFlinger服务的线程模型分析一文可以知道,SurfaceFlinger服务是通过它的UI渲染线程来将应用程序的UI渲染到硬件帧缓冲区中去的,因此,接下来我们就通过分析SurfaceFlinger服务的UI渲染线程的执行过程来分应用程序UI的渲染过程,这个过程如图1所示。
图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所示。
图2 系统显示屏以及应用程序窗口的属性变化处理过程
这个过程可以分为6个步骤,接下来我们就详细分析每一个步骤。
Step 1. SurfaceFlinger.handleTransaction
图3 应用程序窗口设置当前所要渲染的图形缓冲区的过程
这个过程可以分为7个步骤,接下来我们就详细分析每一个步骤。
Step 1. SurfaceFlinger.handlePageFlip
图4 应用程序窗口的可见区域
那么我们就可以将由(0,0)、(0, w)、(0, h)和(w,h)四个点组成的区域称为应用程序窗口的可见区域。
接下来,我们可以在一个应用程序窗口的可见区域挖一个洞出来,如图5所示:
图5 应用程序窗口的透明区域
这时候应用程序窗口真正的可见区域就需要减去中间被挖出来的洞。这个被挖出来的洞就称为应用程序窗口的透明可见区域。
如果应用程序窗口的可见区域的Alpha通道大于0并且小255,那么我们就认为应用程序窗口的可见区域是半透明的。有两种极端情况,即当应用程序窗口的可见区域的Alpha通道等于0或者255的时候。当等于0的时候,我们就认为应用程序窗口的可见区域是透明的,就如图5所示的洞一样,而当等于255的时候,我们就认为应用程序窗口的可见区域是完全不透明的。
上面我们讨论的应用程序窗口的可见区域是基于单个应用程序窗口而言的,当多个应用程序窗口叠加在一起的时候,在讨论一个应用程序窗口的可见区域的时候,就需要考虑位于它上面的其它应用程序窗口的可见区域的影响了。注意,一个应用程序窗口的可见区域只受位于它上面的其它应用程序窗口影响,而不会受到位于它下面的其它的应用程序窗口影响,因此,我们是按照从上到下的顺序来计算系统中各个应用程序窗口的可见区域的。
为了方便描述,我们假设位于一个应用程序窗口上面的所有应用程序窗口组成了一个整体的可见区域(Above Covered Layers),并且这个可见区域与我们所要讨论的应用程序窗口相交,即它们叠加在一起,如图6所示:
图6 应用程序窗口的被覆盖区域
由蓝色矩形组成的区域即为上层所有应用程序窗口所组成的一个整体可见区域,这个整体可见区域与下面绿色矩形组成的一个应用程序窗口相交的部分,即由虚线所围成的区域,就是下面的一个应用程序窗口的被覆盖区域。
一个应用程序窗口的被覆盖区域有可能是半透明的,也有可能是完全不透明的,但是不可能是透明的,如图7所示:
图7 应用程序窗口的被覆盖完全不透明区域
在原来由虚线围成的区域中,深蓝色的那部分区域就是完全不透明的(Above Opaque Layers),这时候由绿色矩形组成的应用程序窗口的可见区域除了要减去中间的洞(透明区域)之外,还要减去被覆盖的完全不透明区域,如下图8所示:
图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架构打下坚实的基础!