一、图形系统简介图形系统是计算机中最重要的子系统之一。我们平时使用的电脑、手机都是图形界面。对于普通人来说,没有图形界面的电脑几乎没有用。今天我们将讨论图形系统背后的原理。1.1图形系统的诞生早期的计算机没有图形界面,都是命令行界面。每个人都坐在终端前输入命令,执行命令,等待命令完成,等等。这样的电脑更适合科研人员和科技人使用,但不可能普及到千家万户。后来,施乐帕洛阿尔托研究中心(XeroxPARC)率先研究了具有图形界面的计算机,并提出了WIMP的概念。WIMP是Window(窗口)、Icon(图标)、Menu(菜单)、Pointer(指针/鼠标)。我们当前的计算机仍处于WIMP模式。遗憾的是,施乐并没有做出带有图形界面的电脑,而是被乔布斯和比尔盖茨发扬光大了。当乔布斯参观帕克研究中心时,他被他们展示的图形界面惊呆了。回来后,他马上在公司里搞了一个图形界面的操作系统。比尔盖茨发现苹果的图形界面确实不错,他也开始做自己的图形界面,于是就有了Windows系统。后来苹果和微软因为图形界面的问题打了官司。1.2图形系统的总体结构与命令行模式相比,图形模式的编程方式和软件结构都发生了很大的变化。在命令行模式下,程序员只需考虑程序本身的进程,然后通过标准输入输出与终端打交道即可。但是当谈到图形模式时,一切都变了。程序员首先要考虑如何绘制程序的界面,然后通过消息循环处理程序的点击等各种事件。不仅程序员的编程方式发生了变化,操作系统的实现方式也发生了很大的变化。在命令行模式下,操作系统只需要提供一个shell,shell不断的读取和执行命令。但是在图形方式下,操作系统首先要提供一个桌面作为用户使用计算机的起点,同时还要提供一个文件管理器,方便用户查看和管理文件。对于程序员来说,操作系统还提供了图形编程接口,提供了渲染库,还负责合成和显示所有窗口。于是在操作系统中诞生了一个重要而庞大的子系统——图形系统。根据前面几句的描述,我们先来看一下图形系统的简单结构。可以看出图形系统的整体结构还是比较简单的。GUI进程需要窗口系统创建和管理窗口,渲染系统帮助绘制界面,最后显示系统将画面显示在监视器上。1.3图形系统各层的作用了解了图形系统的总体结构,下面详细介绍一下各层的功能。窗口系统一般作为一个进程运行在用户空间,我们称它的进程为DisplayServer。窗口系统有两个职责:一个是窗口管理器,负责窗口的创建、缩放和销毁;另一个是合成管理器,负责将每个GUI进程绘制成位图后的窗口进行合成,然后发送给显示系统进行显示。渲染系统以so库的形式存在,加载到各个GUI进程的内存空间中。渲染系统负责执行GUI进程的绘图命令,并在窗口的显示缓冲区上生成相应的位图。渲染分为2D渲染和3D渲染。2D渲染一般由CPU执行,3D渲染一般由GPU执行。但现在2D渲染通常也由GPU完成。但是很多普通的程序并不直接使用渲染库,而是使用控件库,因为直接使用渲染库太麻烦了。比如我们要绘制一个按钮,使用渲染库API绘制很麻烦,但是如果使用控件库API,我们只需要指定position,size,和风格。显示系统以驱动的形式存在于内核中,驱动是屏幕控制器的驱动或DPU的驱动。显示系统的作用是在监视器上显示一个由所有窗口组成的位图。早期的显示驱动模型是FBDEV,它是针对屏幕控制器的。屏幕控制器没有计算能力,只能接收窗口系统合成的位图进行显示。这时候窗口系统的合成管理器就会使用渲染系统来合成各个窗口的位图,合成也可以看作是一种特殊的渲染。后来屏幕控制器逐渐发展成为DPU,具有计算能力,可以进行复合运算。这时,一种新的显示驱动模型DRM也诞生了。DRM让窗口系统不进行合成操作,而是将每个窗口的显存发送给自己,通过DPU进行合成操作,然后发送到显示器显示。2.Android图形系统Android是目前最流行的移动操作系统之一。今天我们就来详细分析一下Android图形系统。2.1框架概述在谈Android之前,我们先来看看Linux发行版的图形系统。由于Android内核也是Linux,所以它们的显示系统是一样的。Linux渲染系统使用OpenGL和最新的Vulkan,控制库使用GTK(GNOME)或Qt(KDE)。Linux的窗口系统有着悠久而复杂的历史,可以追溯到UNIX时代。这里不细说,只说现状。在Linux上,窗口系统的协议和实现是明确分开的。Linux长期使用的窗口协议称为XWindow,实现是X.org。但是由于XWindow过于陈旧,很多设计不符合现状,历史包袱依然很重。因此,有人设计了一个新的窗口协议Wayland,Wayland最流行的实现叫做Weston。现在大多数Linux发行版都开始转向Wayland/Weston。了解了Linux发行版的图形系统,我们再来看看Android的图形系统。Android的图形系统并没有明确的约定来实现这两个协议。这是因为Linux系统是一个标准的开源系统。很多事情喜欢先定一个协议,然后任何人都可以执行这个协议。Android虽然也是开源的,但是是谷歌直接实现,其他厂商使用的,所以不需要建立协议。Android图形系统在具体细节上与Linux图形系统有很大的不同。这是因为Linux图形系统是面向桌面系统的,而Android图形系统是面向移动系统的。两者的使用环境和开发环境不同,导致不同的实现细节。最大的区别之一是Android图形系统中没有典型的窗口概念。在其他的窗口系统中,一般都有一个CreateWindow接口用来创建一个窗口,返回值是一个窗口句柄,然后我们就可以使用这个窗口句柄来做其他的事情了。但是在Android中,没有这样的逻辑。窗口的概念在具体实现中是隐藏和散布的。程序员面对的是Activity、View、ViewGroup。以下小节将介绍Android图形系统的各个部分。2.2渲染系统概述在Android中,一开始是用OpenGLES做3D渲染,用skia做2D软件渲染。后来为了优化2D渲染,开发了硬件渲染hwui。hwui是OpenGLES的一个包。后来hwui调用了skia,skia封装了OpenGLES进行硬件渲染。当然,skia也保留了软件渲染部分。让我们看看下面的图片。OpenGLESsystemwrapper是系统提供的标准接口库。它的so位置是固定的,方便程序加载。其界面为标准界面,方便程序使用。但是它本身并没有任何实现逻辑,所有的实现逻辑都在GPU厂商提供的非开源库中。普通的APK并不直接使用这些渲染库,而是使用系统提供的控件库。Android提供的大部分控件都在android.view和android.widget包中。2.3窗口系统概述窗口系统有两个职责,窗口管理器和组合管理器。在Android中,两者并不在一起。窗口管理器是在system_server进程中实现的,名字叫WindowManagerService(WMS),是Java语言实现的,因为system_server是Java进程。为什么Android要在system_server中实现WindowManagerService?这是system_server中的ActivityManagerService(AMS)。两者关系比较密切,放在一起比较合适。组合管理器在一个名为SurfaceFlinger的单独进程中实现。一开始直接合成了SurfaceFlinger。后来由于硬件合成的兴起,SurfaceFlinger不再直接进行合成操作,而是将合成操作转发给底层。WindowManagerService和SurfaceFlinger使用Binder进程间通信进行交互。让我们看下图:谷歌推出了一个名为HWC(硬件合成器)的模块来处理硬件合成。最开始HWC只是一个so库,运行在SurfaceFlinger进程中。后来HWC独立成了一个单独的进程。在HWC中,有很多厂商提供的非开源和半开源库。这张图片中的图片和APK没有交互。window系统与APK的交互分为两部分。一是程序在创建Activity的时候会和WMS交互创建一个窗口。Android中没有典型的窗口概念,PhoneWindow、DecorView、ViewRootImpl、Surface可以组合在一起作为窗口的概念。还有一部分没有画出来,就是APK渲染和SurfaceFlinger合成的生产者消费者关系。这个逻辑将在下一章讨论。如果想深入学习AMS,推荐阅读老罗Android之旅中的WMS文章:https://blog.csdn.net/Luoshengyang/article/details/8462738,以及袁慧慧写的WMS解析:http://githuan.com/2017/01/08/windowmanger/2.4显示系统概述显示系统与屏幕直接相关,属于内核中的驱动程序。对于任何一种硬件,内核一般都有一个驱动模型,所有的硬件厂商都基于这个硬件模型开发驱动。FBDEV首先被添加到从显示中抽象出来的驱动程序模型中。后来随着硬件和软件的发展,诞生了新的驱动模型DRM。现在大部分系统都在转向DRM,所以我们这里说说DRM。先画个图看看:这个结构体其实就是很多驱动的一个结构体。内核定义并实现了DRMCore,硬件厂商根据DRMCore的要求扩展结构,实现函数指针,然后调用注册函数对自己进行注册。用户空间使用的硬件会创建一个设备文件。用户空间可以打开设备文件,使用ioctl调用各种命令。ioctl命令由Core定义,具体的驱动需要实现这些命令。直接在用户空间使用ioctl命令还是比较麻烦的,所以会有一个libdrm库,将各种ioctl命令封装起来,转换成函数接口,这样使用起来更方便。3.生产者-消费者模型在说渲染和合成之前,我们先说说它们之间的关系,以及它们交互的过程。3.1概述渲染和合成是生产者和消费者的关系,那么它们是如何交互的呢?Android实现了一个生产者-消费者模型BufferQueue,生产者和消费者通过BufferQueue进行交互。BufferQueue管理GraphicBuffer,producer渲染的内容放在GraphicBuffer上,consumer合成的内容的来源来自GraphicBuffer。GraphicBuffer通过Google定义的Hidl接口Gralloc分配内存,Gralloc通过ION分配内存。ION是一种基于DMA-BUF的跨空间、跨设备的内存分配方式。为了加快产生消费的过程,BufferQueue可以采用异步方式。异步的时候,需要同步步调。为此采用的方法是Fence。Fence是一种跨空间、跨设备的同步机制。跨空间是指进程与进程之间、内核空间与用户空间之间,跨设备是指两个设备的驱动程序之间或驱动程序与进程之间。我们画一张图看看他们的整体关系。3.2BufferQueueBufferQueue是Android中渲染和合成的生产-消费关系模型的实现。我们先来看看如何使用BufferQueue。voidBufferQueue::createBufferQueue(sp
