聊聊 Android 的 GUI 系统( 五 )


我是SufaceFlinger我是由 init 进程所启动的守护进程,运行在Android系统的 System 进程中,负责管理Android系统的帧缓冲区(Frame Buffer),需要显示 UI 界面的应用程序需要通过 Binder 服务来与我通信 。每个有 UI 界面的程序都在我这里有相对应的 Client 实例 。应用程序与 Client 间的 Binder 接口是 ISurfaceComposerClient 。Client 也只是我分配给应用程序的一个”代表“ ,真正的图行(Buffer)需要另外申请,即调用 Client 提供的 ISurfaceComposerClient::createSurface()来实现 。同时,在 SufaceFlinger 进程中将会有一个 Layer 被创建,代表了一个画面 。ISurface 就是控制这一面的handle,它将保持在应用程序端的 SufaceControl 中 。
事实上,我是一个耿直boy,你看我的名字就知道,我的职责是 Flinger,即把系统中所以应用程序最终的“绘图结果”进行“混合”,然后统一显示到物理屏幕上 。所以我不会太关注各个应用程序的“绘画过程”,于是我又派出了一个“代表”——BufferQueue 替我去完成这一光荣的使命 。
现在万事俱备,只欠东风,我就可以铆足干劲哗啦啦的绘制了,观众也就能看到美轮美奂的"节目"了 。那东风从哪来?又要到哪去?这时候就轮到我们勤勤恳恳的快递员选手——VSync 大展身手了 。
我是VSync谷歌在4.1版本引入了一个重大的改进——Project Butter,也即是黄油计划 。Project Butter 对 Android Display 系统进行了重构,引入了三个核心元素,即 VSYNC、Triple Buffer 和 Choreographer 。
安卓系统中有 2 种 VSync 信号:屏幕产生的硬件 VSync 和由 SurfaceFlinger 将其转成的软件 Vsync 信号 。采用 Vsync 进行显示同步,一旦 Vsync 信号出现,CPU 便立即开始执行 Buffer 的准备工作 。目前 Android 是采用 Multiple Buffer 的技术来处理的 。
没有引入vsync的情况 

聊聊 Android 的 GUI 系统

文章插图
 
上图是没有引入VSync 机制的处理流程 。可以看出,一个很明显的问题是,只要一次cpu/gpu 处理出现异常就可能导致后面的一系列的处理出现异常
 
引入VSync 机制 
聊聊 Android 的 GUI 系统

文章插图
 
上图是引入 VSync 机制的后的处理流程 。在 FPS < 手机屏幕刷新率的情况下,一切运行完美
 
Double Buffering 异常情况 
聊聊 Android 的 GUI 系统

文章插图
 
 
上图是在 VSync 机制下,Double Buffering 时 FPS > 手机屏幕刷新率的情况 。只要出现一次 Jank 就会影响下一次的 VSync (cpu 不能工作)
Triple Buffering 异常情况 
聊聊 Android 的 GUI 系统

文章插图
 
 
上图是在 VSync 机制下,Triple Buffering 时FPS > 手机屏幕刷新率的情况 。当第一次 VSync 发生后,CPU 不用再等待了,除了第一次的 Jank 无法规避,第二次、第三次 VSync 到来时都能有效采用到 buffer,从而有效降低了系统显示错误 。
VSync 最终会被 EventThread::threadLoop()分发给各监听者,如 SurfaceFlinger 进程中就是 MessageQueue。VSync 被 SurfaceFlinger 监听到后,SurfaceFlinger 首先需要遍历 当前 Layer (这里的 Layer 对应的则是 BufferQueue) ,确定是否需要重绘 。对应 Z-order 等与编排相关的 SurfaceFlinger可以自己确定,但是对于各个 Buffer 内容的变动,还是需要更加专业的 BufferQueue 来处理了 。BufferQueue 处理完成,并且将结果返回给 SurfaceFlinger 后,再由 SurfaceFlinger 进行“加工混合”,交由 OpenGL ES 显示出来。
我是Choreographer字面翻译过来,我是编舞者的意思 。具体来讲,我主要是配合 Vsync(因为我可以监听底层Vsync信号) ,给上层 App 的渲染提供一个稳定的 Message 处理的时机 。
ViewRootImpl 启动时会初始化 Choreographer 的实例 。
当 Vsync 信号由 SurfaceFlinger 中创建 HWC 触发,唤醒 DispSyncThread 线程,再到 EventThread 线程,然后再通过 BitTube(一种进程间通信的一种机制) 直接传递到目标进程所对应的目标线程,执行 handleEvent方法 ,然后通过 C++ 层的 dispatchVsync 进入到 java 层的 dispatchVsync 回调,触发FrameDisplayEventReceiver.run() 如此 Choreographer 便接收到了消息,doFrame()执行,UI 绘制开始 。
参考文献