一、前言:
Android是基于Linux的,而显示设备的驱动也都是和Linux普通设备一样去管理,也就是说归根结底还是要按照Linux的方式组织数据送给LCD,因此,我们理解Android设计的这一套复杂的显示系统时候,一定要提醒自己目的地在哪儿!
二、显示系统逻辑:
这儿先画个很简单的逻辑图,帮助大家理解:
- APP不断生产数据到内存的
FrameBuffer
中,CPU控制FrameBuffer
中的数据定期送给LCD即可; - LCD上的一个像素在
FrameBuffer
中用两个字节描述,比如LCD要显示1920*1080
大小图片,那么FrameBuffer分配的大小就应该是1920*1080*2
字节;
大家不妨思考两分钟,这个系统最大的问题在哪儿?
...Two minutes later...
最大问题就是只有一个FrameBuffer
,朋友请你想一想,APP和LCD同时在访问同一个FrameBuffer
,这不就相当于你当着用户的面明目张胆地画图吗?用户火气大一点,你设备都得被摔稀碎。这样就会导致:
- 如果你APP写得快,LCD读的慢,这样就有可能会跳帧,比如:原来的画面是123,APP写完1之后LCD显示了,当APP写完2的时候LCD还没来读,APP写完3的时候LCD又来读了,因此,LCD显示出来的是13;
- 如果APP写得太慢,LCD读的太快,同理,就是卡帧,因此你太慢了;
- 如果APP写得刚好等于LCD读的速度,那么不会有问题,可实际场景中,这种理想化场景几乎不可能一直保持;
那么,怎么改进呢?
就是采用多个FrameBuffer
,如下图所示:
-
当APP写
FrameBuffer1
的时候,LCD在读取FrameBuffer2
; -
完成之后,会将
FrameBuffer1
和FrameBuffer2
进行swap(交换); -
然后App写
FrameBuffer2
,LCD读FrameBuffer1
;
一直这样循环即可大功告成!!!
二、Layer:
想象下,如果每个APP都跑去绘图会有什么问题?比如你在小窗口刷视频网站,此时,最顶端的状态栏,最下端的导航栏,以及视频播放窗口这三个都不停去送显,但是状态栏和导航栏几乎是不变的,只有APP的自己视频窗口内容一直在变,他们三个都送显会浪费系统资源。因此,引入Layer(图层)的概念,比如我们可以将状态栏和导航栏打包成一个Layer,让SystemUI
进程去负责刷新,APP单独一个Layer去刷新数据,这样,每个Layer在底层都有自己的FrameBuffer
,上层只管往里写,最终通过SurfaceFlinger
控制HWC去合成为一张图片送显。
三、APP到SurfaceFlinger:
上面了解了一个很潦草的框架图,下面就详细分析下数据怎么准备的,又是最终怎么送给FrameBuffer
的。
整个显示系统可以用一个很关键的模块串起来,那就是大名鼎鼎的SurfaceFlinger
(下文可能会因为懒,写成SF),它的主要作用就是对多个App的,我们先看一个架构图:
这个图我的重点是在App给SurfaceFlinger
送的数据是如何准备的:
- 每个APP都有1到多个buffer,这个buffer是从SF申请来的,SF传给APP的是fd,APP进行mmap即可获得对应buffer;
- APP拿到buffer之后,可以直接往buffer写数据,可以通过2D引擎库(比如skia)或者3D引擎库(OpenGL ES)去准备数据(就是渲染);
- 准备好数据之后写入到Surface当中(其实上面说的buffer由surface在管理);
- 这样,每个APP都写好了自己的Surface,Android会将这多个Surface通过Z-Order(前后顺序)结合Layout(布局)进行合成为一张图片;
- 当然SF并不是啥都自己干,它可以通过Gralloc去管理内存,可以通过HWC去合成Surface,也可以调用OpenGL ES去合成;
四、SurfaceFlinger:
SurfaceFlinger
作为Android显示系统的核心管理者,涉及的模块特别多,我们先理清楚几个关键模块之间的关系,否则,会只见树木不见森林。
-
主要模块:
-
SurfaceFlinger:将Surface合成起来,管理着两个驱动程序ashmem和frameBuffer;
-
Ashmem:匿名内存模块;
-
Gralloc:负责帧buffer的分配和释放;
-
EGL:为了保证OpenGL ES的平台无关性,封装EGL;
-
OpenGL ES:OpenGL不是一个库,是一种接口协议,如果有gpu会加载gpu对应的libhgl库,如果没有gpu,也可以加载libagl这个纯软件库,通过cpu去完成,具体应该加载软件还是硬件库,会在egl.cfg这个配置文件中配置好;
-
FramebufferNaitveWindow:OpenGL ES是平台无关的,因此,需要在不同的平台系统上进行"本地化",Android上就是通过
FramebufferNaitveWindow
这个中介将Android本地的窗口和OpenGL ES的窗口关联起来; -
HWCComposer:进行Layer合成和负责Vsync信号的产生和控制;
-
一般情况下芯片都支持硬件HWC;
-
如果不支持HWC,或者大于支持的Layer个数上限,都使用软件(OpenGL ES)进行合成;
-
无论是硬件或者软件合成,都必须遵守EGL接口;
-
-
也可以使用OpenGL库进行合成;
- 如果有GPU,那么加载对应的so,编写sharde程序去用GPU(硬件)合成;
- 没有GPU那么使用纯软件的libGLES_android这个纯软件库去合成;
-
-
Buffer申请:
-
每个App都有自己的界面,需要将界面数据绘制到1个或者多个buffer里面;
-
那么,这些buffer哪儿来呢?如果APP自己申请buffer,填充好数据,再通过socket发送给SF,效率非常低,因此,通过让SF自己分配了,然后通过mmap映射出来,这样APP和SF操作同一块内存,APP写入数据SF会马上可以读到;
-
SF里面会调用HAL层的Gralloc模块进行buffer申请;
-
Gralloc模块又会通过驱动层的ashmem申请匿名内存,然后返回句柄fd给HAL,再传递给SF;
-
SF再通过Binder系统将fd传给App;
-
App往buffer里面写好数据之后,通过SurfaceFlinger交给FrameBuffer,才可以显示数据;
-
五、总结:
本文主要从框架层面对Android显示系统进行了介绍,里面最重要的就是SurfaceFlinger
,它管理者众多模块完成了Android每一帧画面的显示。