Android显示图形架构学习报告
为方便描述,使用下面的显示系统架构图自上向下对显示架构进行描述。
上图有3个矩形框,最上面的矩形框是应用级别的,主要使用Java语言编写;中间的矩形框是系统级别的,主要使用C/C++语言编写;最下面的矩形框代表Linux驱动级别的。
在应用级别,ViewTree的根节点是DecorView,而ViewRoot对应ViewRootImpl类,是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot完成,Android的界面刷新事件是由RootView分发的。
应用开发者可以使用Canvas或OpenGL将图像绘制到屏幕上。Canvas渲染的低级实现由Skia图形库提供,现已进行硬件加速。为了确保两个客户端不会同时更新某个缓冲区,或者在该缓冲区正在被显示时写入该缓冲区,必须锁定该缓冲区才能进行访问。LockCanvas()可锁定该缓冲区并返回用于绘制的Canvas,unlockCanvasAndPost()则解锁该缓冲区并将其发送到合成器,当为某个Canvas将相关Surface锁定,则无法使用GLES在该Surface上进行绘制或从视频解码器向其发送帧。在OpenGL渲染中,OpenGL ES定义了一个渲染图形的API,EGL定义了与之对应的窗口系统。EGLSurface为GLES提供了一个绘制的地方。EGL不提供锁定/解锁调用,而是在发出绘制命令后调用eglSwapBuffers()来提交当前帧。
HWUI主要是android用于2D硬件绘图而加入的一个模块,在HWUI之前,Android主要是用skia来进行软件绘制,后由于绘制性能等问题,现在Android的绘图几乎都是使用了HWUI硬件加速绘图。HWUI主要则是使用OpenGL ES来进行GPU硬件绘图,提升整个系统的绘制性能,主要有三种方式:直接渲染,显示列表渲染和延时渲染列表。
WindowManager是控制窗口的Android系统服务,是视图容器。窗口总是由Surface提供支持。该服务会监督生命周期、输入和聚焦事件、屏幕方向、转化、动画、位置、变形、Z-Order 以及窗口的其他许多方面。窗口管理器会将所有窗口元数据发送到 SurfaceFlinger,以便 SurfaceFlinger 可以使用该数据在显示部分合成 Surface。
在图像显示架构中,使用了生产者-消费者模式。图像流生产方是生成图形缓冲区以供消耗的任何内容,如上述的Canvas、OpenGL ES和media server视频解码器。对于Canvas,当锁定一个Surface以便访问Canvas时,“CPU渲染器”将连接到 BufferQueue 的生产方,直到 Surface 被销毁时才会断开连接,在此期间,无法使用GLES在该Surface上进行绘制或从视频解码器向其发送帧。
对于OpenGL ES,当通过eglCreateWindowSurface()调用时,EGL将创建一个新的EGLSurface对象,并将其连接到窗口对象的BufferQueue的生产接口方。一个生产接口方连接到一个BufferQueue,当销毁EGLSurface对象时,它将与BufferQueue断开连接,并运行其他内容连接到该BufferQueue。
图像流的最常见消耗方是SurfaceFlinger,SurfaceFlinger 接受来自多个来源的数据缓冲区,对它们进行合成,然后发送到显示设备。当应用进入前台时,WindowManager 服务会向 SurfaceFlinger 请求一个绘图 Surface。SurfaceFlinger 会创建一个其主要组件为 BufferQueue 的层,而 SurfaceFlinger 是其消耗方。生产方端的 Binder 对象通过 WindowManager 传递到应用,然后应用可以开始直接将帧发送到SurfaceFlinger 。状态栏和导航栏由系统进程渲染,而应用层由应用渲染,两者之间不进行协调。SurfaceFlinger 在收集可见层的所有缓冲区之后,便会询问 Hardware Composer 应如何进行合成。
Hardware Composer HAL (HWC) 用于确定通过可用硬件来合成缓冲区的最有效方法。作为 HAL,其实现是特定于设备的,而且通常由显示设备硬件原始设备制造商 (OEM) 完成。在使用叠加平面时,可以将多个缓冲区全部传送到显示硬件,并指示它从不同的缓冲区读取屏幕不同部分的数据。HWC会执行以下计算:
1.SurfaceFlinger向HWC提供一个完整的层列表,并询问“您希望如何处理这些层?”2.HWC 的响应方式是将每个层标记为叠加层或 GLES 合成。3.SurfaceFlinger 会处理所有 GLES 合成,将输出缓冲区传送到 HWC,并让 HWC 处理其余部分。如下图所示:
由于硬件供应商可以定制决策代码,因此可以在每台设备上实现最佳性能。
总之,SurfaceFlinger 和 Hardware Composer HAL 通过执行以下四项关键任务来准备用于显示的图形数据缓冲区:
BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。BufferQueue通信过程如下图所示:
生产方请求一个可用的缓冲区 (dequeueBuffer()),并指定一组特性,包括宽度、高度、像素格式和用法标记。生产方填充缓冲区并将其返回到队列 (queueBuffer())。随后,消耗方获取该缓冲区 (acquireBuffer()) 并使用该缓冲区的内容。当消耗方操作完毕后,将该缓冲区返回到队列 (releaseBuffer())。BufferQueue 是将缓冲区池与队列相结合的数据结构,它使用 Binder IPC 在进程之间传递缓冲区。SurfaceFlinger 不会在应用每次提交缓冲区时都执行操作,而是在显示设备准备好接收新的缓冲区时才会唤醒。
Gralloc是图形内存分配器,分配图像生产方请求的内存(缓冲区)。
显示刷新机制:
Android4.1后,显示系统引入了三个核心元素:VSYNC, Tripple Buffer和Choreographer。VSYNC是Vertical Synchronized的缩写,是一种定时中断;Tripple Buffer是显示数据的缓冲区;Choreographer起调度作用,将绘制工作统一到VSYNC的某个时间点上,使应用的绘制工作有序进行。
Android在绘制UI时,会采用一种称为“双缓冲”的技术,双缓冲即使用两个缓冲区(在SharedBufferStack中),其中一个称为Front Buffer,另外一个称为Back Buffer。UI总是先在Back Buffer中绘制,然后再和Front Buffer交换,渲染到显示设备中。理想情况下,一个刷新会在16ms内完成(60FPS),下图就是描述的这样一个刷新过程(Display处理前Front Buffer,CPU、GPU处理Back Buffer。
1.没有VSYNC信号同步时
在进入第3个16ms时,Display应该显示第2帧数据,但由于CPU和GPU还没有处理完第2帧数据,故Display只能继续显示第一帧数据,结果使得第1帧多画了一次,导致错过了显示第二帧。产生这个问题的原因是CPU可能在第1个16ms内因为在忙别的事情,不知道该到处理UI绘制的时间了而没有及时处理第2帧数据。为解决这个问题,引入了VSYNC,核心目的是解决刷新不同步的问题。
2.引入VSYNC信号同步后
上图中仍存在一个问题,如果CPU/GPU的FPS小于Display的FPS,会发生如下图的情况:
在第二个16ms时间段,Display本应显示B帧,但因为GPU还在处理B帧,导致A帧重复显示。同理,在第二个时间段内,CPU无所事事,因为A Buffer被Display在使用。B Buffer被GPU使用。一旦过了VSYNC时间点,CPU就不能被触发以处理绘制工作了。为什么CPU不能在第二个16ms处开始绘制工作呢?原因就是只有两个Buffer(Android 4.1之前)。如果有第三个Buffer的存在,CPU就能直接使用它,而不至于空闲。于是在Android4.1以后,引出了第三个缓冲区:Tripple Buffer。Tripple Buffer利用CPU/GPU的空闲等待时间提前准备好数据,并不一定会使用。
3.引入Tripple Buffer后:
上图中,第二个16ms时间段,CPU使用C Buffer绘图。虽然还是会多显示A帧一次,但后续显示就比较顺畅了。
参考引用:https://www.jianshu.com/p/b1b75ab6f17f
https://source.android.com/devices/graphics
https://blog.csdn.net/wind_hzx/article/details/20307093
https://zhuanlan.zhihu.com/p/27344882