前言
GUI(Graphical User Interface)即“图形用户界面”,可以说在任何Operating System中都占据着非常重要的位置,因为它是用户对操作系统最直接的“感官”体验。一款优秀的图形界面系统至少要满足以下几个条件。
流畅性
评判一款GUI系统优劣的重要准则之一,就是流畅性。历史经验表明,即便再“炫酷”的UI界面,一旦出现经常性的画面“滞后”,最终都会被用户多抛弃。
友好性
因为GUI系统是面向终端用户的,可以认为是操作系统的“脸面”。友好性意味着用户操作上的人性化,图形的一致性以及视觉元素上的合理搭配等一系列因素,是一个综合的评判标准。
可扩展性
扩展意味着开发者或用户可以在系统内建的基础上无限延伸自己的创意,比如添加新的界面和交互方式、实现更复杂的图形处理功能等。
OpenGL ES 与 EGL
SurfaceFlinger虽然是GUI系统的核心,但从OpenGL ES的角度来讲,其实只能算是一个“应用程序”。
Linux内核提供了统一的framebuffer显示驱动。设备节点/dev/graphics/fb 或者 /dev/fb ,其中fb0表示第一个Monitor,当前系统实现中只用到一个显示屏。
android的HAL层提供了Gralloc,包括fb和gralloc两个设备。前者负责打开内核中的framebuffer、初始化配置,并提供post、setSwapInterval等接口,后者则管理帧缓冲区的分配和释放。这就意味着上层元素只能通过Gralloc来间接访问帧缓冲区,从而保证了系统对framebuffer的有序使用和统一管理。
另外,HAL层的另一重要模块是Composer,它为厂商自定制“UI合成”提供了接口。Composer的直接使用者是SurfaceFlinger中的HWComposer(有两个HWComposer),后者除了管理Composer的HAL模块,还负责VSync信号的产生和控制。VSync是“Project Buffer”工程中加入的一种同步机制,它既可以由硬件产生,也可以通过软件来模拟(VSyncThread)。
由于OpenGL ES是一个通用的函数库,在不同的平台系统上需要被“本地化”——把它与具体平台中的窗口系统建立联系,这样才能保证正常工作。从FramebufferNativeWindow这个名称就能判断出来,它就是负责OpenGL ES在android平台上本地化的中介之一。为OpenGL ES配置本地窗口的是EGL。
OpenGL或者OpenGL ES更多的只是一个接口协议,具体实现既可以采用软件,也可以依托于硬件。一方面,这给产品开发带来了灵活性——我们可以根据成本与市场定位来决定硬件配置,满足用户各种需求;另一方面,既然有多种实现的可能性,那么OpenGL ES在动态运行时的取舍是由EGL来实现的,它会去读取egl.cfg这个配置文件,然后根据用户的设定来动态加载libagl(软件实现)或者libhgl(硬件实现)。
SurfaceFlinger中持有一个成员数组mDisplays来描述系统中支持的各种“显示设备”——具体有哪些Display是由SurfaceFlinger在readyToRun中判断并赋值的,并且DisplayDevice在初始化还将调用eglGetDisplay、eglCreateWindowSurface等接口,并利用EGL来完成对OpenGL ES环境的搭建。
很多模块都可以调用OpenGL ES提供的API,包括SurfaceFlinger、DisplayDivice等。
与OpenGL ES相关的模块可以分为如下几类:
配置类
即帮助OpenGL ES完成配置的,包括EGL,DisplayHardware都可以归为这一类。
依赖类
也就是OpenGL ES要正常运行起来所依赖的“本地化”的东西。
使用类
使用者也就是配置者,如DisplayDevice既扮演了构建OpenGL ES环境的角色,同时也是它的用户。
android的硬件接口HAL
对于android中很多子系统来说(如显示系统、音频系统等),HAL都是必不可少的组成部分——HAL是这些子系统与Linux Kernel驱动之间通信的统一接口。
HAL需要解决如下问题点:
硬件接口抽象
HAL并不是专门针对某个特定的硬件设备来设计的,因而如何从众多类型的设备中提取出它们的共同属性并付诸软件实现是一个关键。
接口的稳定性
可想而知,HAL层的接口是不允许频繁更动的,否则就失去了意义。
灵活的使用方法
HAL需要提供一套灵活的使用方法,以供硬件开发商及上层使用者定制它们的需求。
Gralloc与Framebuffer
Framebuffer是内核系统提供的图形硬件的抽象描述。之所以成为buffer,是因为它也占用了系统存储空间的一部分。由此可见,在“一切都是文件”的Linux系统中,Framebuffer被看成了终端显示设备的“化身”。另外,Framebuffer借助于Linux文件系统向上层应用提供了统一而高效的操作接口,从而让用户空间中运行的程序可以在不做太多修改的情况下去适配多种显示设备——无论它们属于哪家厂商、什么型号,都由Framebuffer内部来兼容。在android系统中,Framebuffer提供的设备问文件节点是/dev/graphics/fb *。因为理论上支持多个屏幕显示,所以fb按数字序号进行排序,即fb0、fb1等。其中第一个fb0是主显示屏幕,必须存在。
android的各子系统通常不会直接使用内核驱动,而是由HAL层来间接引用底层架构。显示系统也同样如此——它借助于HAL层来操作帧缓冲区,而完成这一中介任务的就是Gralloc。
Gralloc对应的模块是由FramebufferNativeWindow在构造函数中加载的。Gralloc对应的HAL库被成功加载后,可以给上层提供一些重要的接口。Gralloc的open接口可以帮助上层使用者打开两种设备,分别是fb0和gpu0,fb0就是指“主屏幕”,gpu0负责图形缓冲区的分配和释放。
android中的本地窗口
根据整个android系统的GUI设计理念,至少需要两种本地窗口。
面向管理者(SurfaceFlinger)
既然SurfaceFlinger扮演了系统中所有UI界面的管理者,那么它无可厚非需要直接或间接地持有“本地窗口”,这个窗口就是FramebufferNativeWindow。
面向应用程序
这类本地窗口就是Surface。实际上是从内存缓冲区中分配的空间。
在这个改进的窗口系统中,我们有了两类本地窗口,即Window-1和Window-2。第一种窗口是能直接显示在终端屏幕上的——它使用了帧缓冲区,而后一种Window实际上是从内存缓冲区分配的空间。当系统中存在多个应用程序时,这能保证它们都可以获得一个“本地窗口”,并且这些窗口最终也能显示到屏幕上——SurfaceFlinger会收集所有程序的显示需求,对它们做统一的图像混合操作(有点类似于AudioFlinger),然后输出到自己的Window-1上。