在进入GUI系统的学习前,建议大家可以先阅读本书应用篇中的“OpenGLES”章节,并参阅OpenGL ES官方指南。因为Android的GUI系统是基于OpenGL/EGL来实现的,如果没有一定基础的话,分析源码时有可能会“事倍功半”。
1.1 OpenGLES与EGL
SurfaceFlinger虽然是GUI的核心,但相对于OpenGL ES来讲,它其实只是一个“应用”。
对于没有做过OpenGLES开发的人来讲,理解这部分的内容还是有一定难度的,特别是容易对系统中既有EGL/OpenGLES,又有SurfaceFlinger、GraphicPlane、DisplayHardware、Gralloc、FramebufferNativeWindow等一系列陌生的模块感到混乱而无序。
的确如此,假如不先理清这些模块的相互关系,对于我们深入研究整个Android显示系统就是一个很大的障碍。有鉴于此,我们先来从框架的高度审视一下它们之间看似错综复杂、剪不断理还乱的依赖。
图 11‑1 SurfaceFlinger与OpenGLES等模块关系
我们根据上面这个图,由底层往上层来逐步分析整个架构:
1. Linux内核提供了统一的framebuffer显示驱动,设备节点/dev/graphics/fb*或者/dev/fb*,以fb0表示第一个Monitor,当前实现中只用到了一个显示屏
2. Android的HAL层提供了Gralloc,分为fb和gralloc两个设备。前者负责打开内核中的framebuffer、初始化配置,以及提供post、setSwapInterval等操作,后者则管理帧缓冲区的分配和释放。上层只能通过Gralloc访问帧缓冲区,这样一来就实现了有序的封装保护
3. 由于OpenGL ES是一个通用的函数库,在不同的平台系统上需要被“本地化”——即把它与具体平台上的窗口系统建立起关联,这样才能保证它正常工作。从FramebufferNativeWindow这个名称就能判断出来,它就是将OpenGL ES在Android平台上本地化的中介之一。后面我们还会看到应用程序端所使用的另一个“本地窗口”。为OpengGL ES配置本地窗口的是EGL
4. OpenGL或者OpenGL ES 更多的只是一个接口协议,实现上既可以采用软件,也能依托于硬件。这一方面给产品开发带来了灵活性,我们可以根据成本与市场定位来决定具体的硬件设计,从而达到很好的定制需求;另一方面,既然有多种实现的可能,那么OpenGL ES在运行时是如何取舍的呢?这也是EGL的作用之一。它会去读取egl.cfg这个配置文件,然后根据用户的设定来动态加载libagl(软件实现)或者libhgl(硬件实现)。然后上层才可以正常使用各种glXXX接口
5. SurfaceFlinger中持有一个GraphicPlane成员变量mGraphicPlanes来描述“显示屏”;GraphicPlane类中又包含了一个DisplayHardware对象实例(mHw)。具体是在SurfaceFlinger::readyToRun中,完成对它们的创建与初始化。并且DisplayHardware在初始化时还将调用eglInitialize、eglCreateWindowSurface等接口,利用EGL来完成对OpenGLES环境的搭建。其中:
surface =eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
mNativeWindow 就是一个FramebufferNativeWindow对象。DisplayHardware为OpenGL ES设置了“本地化”所需的窗口
6. 很多模块都可以调用OpenGLES提供的API(这些接口以“gl”为前缀,比如glViewport、glClear、glMatrixMode、glLoadIdentity等等),包括SurfaceFlinger、DisplayHardware等
7. 与OpenGL ES相关的模块,可以分为如下几类:
Ø 配置类
即帮助OpenGL ES完成配置的,包括EGL、DisplayHardware都可以认为是这一类
Ø 依赖类
也就是OpenGL ES要正常运行起来所依赖的“本地化”的东西,上图中是指FramebufferNativeWindow
Ø 使用类
使用者也可能是配置者,比如DisplayHardware既扮演了“帮助”OpenGL的角色,同时它也是其使用方。另外只要处在与OpenGL ES同一个环境(Context)中的模块,都可以使用它来完成操作,比如SurfaceFlinger
应用程序的图形缓冲区是请求surfaceflinger来分配,一般而言是从内存中分配,分配的时候surfaceflinger会创建匿名共享内存并且映射到自己的进程空间,然后应用端通过registerbuffer来吧匿名共享内存映射到自己空间,在HAL层是通过gralloc设备分配的
fb设备在打开的时候也会分配缓存区,脚系统帧缓存区,这个缓存区和framebuffer不是一个概念,但是这个缓存区可以从framebuffer中分配,也可以从内存或者其他地方分配
渲染过程就是吧系统帧缓存区渲染到framebuffer过程
surfaceflinger只是一个服务,只是简单的执行应用端发来的指令而执行相关的流程,
一、Overview
上图的原型取自高通的文档,由于原图无法描述现有的架构,我在原图的基础了做了些修改,主要是增加了overlay部分,另外其他部分根据现有的软件也做了些许改动。下面先对上图做个大概的介绍,后面会针对重点部分做详细的分析。 最上面一层为应用程序,根据数据类型以及应用的不同可以分为几种。 第一种是最普通的应用,如UI界面的显示,这部分通常数据类型为RGB格式,数据无须再经过特殊的处理。该应用可以说遍布各个应用程序,几乎是实时存在的。 第二种是针对大块YUV数据的应用,如camera的preview、视频的播放等。该应用只针对特定的应用程序,开启时通过overlay直接把大块的YUV数据送到kernel显示。 第三种其实和第一种类似,只不过由于应用的需求在显示之前需要对数据进行2D、3D的处理(使用OpenGL、OpenVG、SVG、SKIA),处理之后的流程和普通的显示就没什么差别了。一般在Game、地图、Flash等应用中会用到。 应用之下是framework,其中最核心的就是surfaceflinger了,它为所有的应用程序的显示提供服务。由于overlay的接口挂在surfaceflinger里面(虽然2者在功能上不相干),所有使用overlay的AP需要通过surfaceflinger才可以访问overlay;另外,由于surfaceflinger需要使用OpenGL来compose surface,这也就是为什么surfacelfinger会调用EGL wrapper了,EGL wrapper是对Graphics HAL的封装,除了surfaceflinger会调用它来compose surface外,上层的2D、3D应用也会调用它来进行图形处理。 再下一层就是HAL了。 首先一个是overlay模块,对上提供control channel和data channel;对下则通过系统调用到kernel中的MDP driver。 再一个是Gralloc模块,注意它是和overlay并列的,它包含2个部分,一部分是为上层提供pmem的接口,另一部分则是对framebuffer进行刷新,这里的framebuffer其实就是UI的数据。由此可见上层有2个通道把显示数据送到kernel中,framebuffer是传统的方式,overlay是android(éclair以后)后增加的。 红色及右边部分是OpenGL的HAL,其中红色部分代表HW solution,高通提供的,这部分是没有源码的;右边的software graphics library是SW solution,android自身的。HW和SW solution可以同时存在也可以只有一个,后面会讲解。 再往下就是kernel中的driver了,最主要的就是fb设备驱动以及MDP4 overlay的驱动,从硬件上看2者是并列的,framebuffer最终也是通过overlay方式送入MDP的。PMEM和KGSL分别对应kernel中pmem的driver(/dev/pmem)和Adreno220的driver。 二、Surfaceflinger详解 1.overview Surfaceflinger可以说是Android显示系统中的核心,在android当中它是一个service,提供系统范围内的surface composer 功能,它能够将各种应用程序的2D 、3D surface 进行组合,合并最终得到的一个main surface数据会送入显存。简单的说,surfaceflinger就像是画布,它不关心画上去的内容,只是一味的执行合成功能,当然要根据画的位置、大小以及效果等参数。这很像Photoshop中的各个Layer,你可以在不同的layer画任意的内容,每个layer可以设置位置、大小、效果参数等,最终通过merge合成一个layer。 从应用的角度看,每个应用程序可能对应一个或多个图形界面,每个界面可以看作是一个surface。首先每个surface有它的位置、大小、内容等元素,这些元素是可以随便变化的;另外不同的surface的位置会有重叠,会涉及到透明度等效果处理问题,这些都是通过surfaceflinger来完成的。当然了,surfaceflinger担任是一个管理的职责,对于效果处理及合成它是通过OpenGL来做的,但前提是surfaceflinger需要把相关参数计算好,如重叠的位置等。 2.Surfaceflinger在系统中的位置 Android中的图形系统采用Client/Server架构。服务端负责Surface的合成等处理工作,客户端提供接口给上层操作自己的Surface,并向服务端发送消息完成实际处理工作。服务端 (即SurfaceFlinger)主要由c++代码编写而成。客户端端代码分为两部分,一部分是由Java提供的供应用使用的api,另一部分则是由c++写成的底层实现。如下图所示:
除去最上层的应用不算,surface最上层的接口就是java surface了,文件路径如下: frameworks/base/core/java/android/view/Surface.java,该文件中的接口会被应用间接调用。 我们从JNI开始看,surface的JNI文件路径如下: frameworks/base/core/jni/android_view_Surface.cpp,里面的接口大概分为2类,一类是负责管理ibinder通信的;另一类才是和显示控制相关的,第二类接口会直接调用C实现函数。 C实现的文件路径如下: frameworks/base/libs/ui/Surface.cpp 我们来看看JNI中一些重要的接口: SurfaceSession_init:本接口只会被调用一次,负责创建surfacecomposerclient,主要为进程间通信做准备。对应的销毁函数有SurfaceSession_destroy和SurfaceSession_kill。 Surface_init:负责创建surface,最终会调用到surfaceflinger中的createSurface,对应的销毁函数有Surface_destroy和Surface_release。 Surface_lockCanvas:当对一个surface进行绘图之前要调用的,将该surface锁定,并且得到surface的back buffer,应用可以绘图。 Surface_unlockCanvasAndPost:当上层绘图完毕后,通过该函数通知底层back buffer已绘制完毕,可以更新到显存中。 Surface_setLayer/ Surface_setPosition/ Surface_setSize/ Surface_hide/ Surface_show/ Surface_setOrientation/ Surface_freeze/ Surface_unfreeze Surface_setFlags/ Surface_setAlpha/ Surface_setMatrix:设置surface的一些属性,如大小、位置、方位、截取范围,Z-order等。其最终改变的都是surface的结构体属性,如下: uint32_t what;//哪一项属性改变 int32_t x;//显示位置 int32_t y; //显示位置 uint32_t z; //layer顺序 uint32_t w;//宽度 uint32_t h;//高度 float alpha;//透明度 uint32_t tint;//色彩,未使用 uint8_t flags;// 标志 uint8_t mask;//屏蔽命令 uint8_t reserved; matrix22_t matrix;//截取范围 Region transparentRegion;//透明度设置 3.JNI与Surfaceflinger的连接通讯 由于JNI及C函数实现与surfaceflinger不在同一个进程(一个在应用端-客户端,另一个在服务端),android中通过IPC(Binder)方式实现进程间通信,下图来源于网上,不过我修改了里面的一些错误,它演示了JNI和surfaceflinger建立连接以及创建surface的流程。
JNI和C函数实现我们看作是一个部分 这里看到一个比较重要的部分——SurfaceComposerClient,它是surfacelinger的客户端,通过它上层才可以和surfaceflinger使用Binder联系到一起,IsurfaceComposer和IsurfaceFlingerClient都是用来实现Binder通信的。具体流程讲解 如下: 应用程序通过JNI接口SurfaceSession_init创建SurfaceComposerClient。通过SurfaceComposerClient函数中调用getComposerService获得IsurfaceComposer的IBinder对象,然后通过这个对象的createConnection又获得IsurfaceFlingerClient的IBinder,通过这个IBinder,JNI就可以调用Surfaceflinger中的接口了,如createSurface。由于采用Binder方式,代码部分稍微复杂一些,需要多看几遍才能把流程理清楚。 4.Surfaceflinger与libui、OpenGL、显示设备的连接 这里不得不提到android对媒体框架中一个很重要的部分,那就是libui,它是一个框架库提供对底层操作的接口,比如会调用Gralloc、Overlay等HAL层接口。其他的库类继承的方式来调用libui,surfaceflinger就是这样和显示设备连接的(包括写显存和对pmem的使用) Surfaceflinger使用OpenGL来合成surface,所以surfaceflinger会直接调用到OpenGL的接口。 它们的架构如下:
这部分的流程比较复杂,主要是各个类的继承绕的比较多,我也是看了很多遍代码以及参考了些资料才理出来,下面来详细解释下这个图: Surfaceflinger在设计时考虑到支持多个屏幕,但目前的版本只支持一个,在surfaceflinger当中一个显示设备对应一个图中的DisplayHardware,surfaceflinger在初始化时会新建Displayhardware(请参考surfaceflinger.cpp中的readyToRun函数),它完成的主要任务一个是建立FramebufferNativeWindow,确定数据输出设备接口(请参考FramebufferNativeWindow.cpp),再一个就是初始化OpenGL,并创建main surface,后续surfaceflinger中所有的layer最终都将被画到这个main surface上(请参考displayhardware.cpp的init函数)。这样main surface、OpenGL和libui中的FramebufferNativeWindow接口就绑定在一起。 由于libEGL负责所有layer的最终合成,所以最后数据送往HAL一定要libEGL来触发,对应的函数流程是: postFrameBuffer(surfaceflinger)->Flip(displayhardware)-> eglSwapBuffers(OpenGL)-> queueBuffer(libui)->fbpost(gralloc) 另外图中的GraphicBuffer是libui中提供的对pmem的操作接口,它会直接调用gralloc模块。关于OpenGL和Gralloc后面会有单独的章节来介绍。 |