VSync 信号、BufferQueue 机制和 SurfaceFlinger 的合成流程
可以把整个Android的图形显示过程想象成一个高效的动画制作工厂,这个工厂的目标就是在屏幕上播放一帧接一帧的画面(比如60帧/秒)
一. VSync 信号 - 工厂的节奏大师
VSync(Vertical Synchronization,垂直同步)信号就像是一个节拍器或者工厂的指挥,它以一种非常稳定、精确的节奏发出“滴答”声。这个“滴答”声规定了两个最重要的时间点:
- 什么时候开始绘制下一帧画面(App绘制节奏)
- 什么时候必须把准备好的画面显示到屏幕上(屏幕刷新节奏)
在标准的60Hz屏幕上,这个“滴答”声每秒响60次,每次间隔约16.6毫秒
为什么需要它?
如果没有这个节拍器,工厂的各个部门(App绘制和屏幕显示)就会各自为政,混乱不堪。最典型的问题就是屏幕撕裂(Tearing):即屏幕上半部分还显示着上一帧的画面,下半部分却已经拉取了新的一帧画面开始显示了。VSync信号就是为了让所有操作同步,避免这种不和谐的情况
在Android中的具体角色:
Android系统中有两种VSync信号:
- App VSync (VSYNC-app): 告诉应用程序:“节拍到了,现在可以开始绘制下一帧了!” 这驱动了UI线程的
measure
、layout
和draw
- SurfaceFlinger VSync (VSYNC-sf): 告诉SurfaceFlinger合成器:“节拍到了,现在该把所有准备好的画面合成了!” 这驱动了合成的开始
总结:VSync是整个图形系统的心脏起搏器,它规定了所有操作的步调和节奏
二. BufferQueue - 生产和消费之间的传送带
BufferQueue是连接生产者(Producer)(如App)和消费者(Consumer)(如SurfaceFlinger)的一个共享队列。它就像是一条有三节传送带的流水线
这条传送带(BufferQueue)上通常放着三个缓冲槽(Buffer Slot),每个槽里可以放一帧图像数据(GraphicBuffer)
- 生产者(App) 在传送带的末端(
dequeued
状态)取一个空槽,装满它生产的图像(变成queued
状态),然后把它放回传送带 - 消费者(SurfaceFlinger) 在传送带的前端取一个已经装满的槽(变成
acquired
状态),把里面的货物(图像数据)拿走使用,用完后把空槽清理干净(变回dequeued
状态),再放回传送带的末端,等待生产者再次使用
Buffer的状态:
- Free / Dequeued: 空缓冲区,生产者可以获取并绘制
- Queued: 生产者已绘制完成,放入队列等待消费者使用
- Acquired: 消费者已取走,正在使用(合成或显示)中
- Posted: 消费者已使用完毕,释放回队列,变回Free状态
为什么需要它?
- 解耦(Decoupling): 生产者和消费者不需要知道对方的存在,也不需要互相等待。App可以尽情地生产帧(只要还有空缓冲区),SurfaceFlinger也可以在有准备好的帧时立刻进行合成。这极大地提高了效率
- 防止撕裂(Tearing): 消费者(显示)永远只使用已经完全准备好的缓冲区,而不会去读一个正在被生产者绘制的半成品缓冲区。这就是双缓冲/三缓冲机制的核心
总结:BufferQueue是图形系统中最重要的基石,它用一种高效、安全的方式实现了生产者(App)和消费者(显示)之间的数据共享和解耦
三. SurfaceFlinger 合成流程 - 最后的装配车间
SurfaceFlinger是Android的显示合成器。它的工作就像是一个装配车间的老师傅
它的面前有很多个传送带(BufferQueue),每条传送带都来自一个不同的应用程序或界面层(比如状态栏、导航栏、Activity界面、壁纸等)。每条传送带都会源源不断地送来它们各自绘制好的画面(Buffer)
老师傅(SurfaceFlinger)的工作就是:
- 等待指令(VSYNC-sf): 他听着节拍器(VSync信号)。节拍一响,他就开始一次新的工作循环
- 收集物料(acquireBuffer): 他去每条传送带的前端看看,如果有已经准备好的画面(Buffer),就拿过来
- 设计装配图(计算合成): 他根据客户的订单(每个Layer的位置、大小、透明度、Z-order等属性),决定如何把这些画面一层层地叠起来。比如先把壁纸铺底,然后盖上App的窗口,最后在最上面贴上状态栏
- 装配(compose):
○ 过去(低效方式): 老师傅用CPU把所有的画面数据拷贝、混合到一起,生成一张最终的大图(FrameBuffer)。这个过程很耗CPU资源
○ 现在(高效方式): 老师傅现在有个厉害的助手叫GPU(或者更专业的硬件合成器HWC)。老师傅只需要告诉GPU:“你把A画面放在(0,0)位置,B画面放在(100,100)位置,C画面半透明...” ,GPU就会以极高的并行速度完成这个“拼图”工作,几乎不消耗CPU资源。HWC是手机SoC里的一个专用硬件模块,做合成这件事比GPU还省电 - 送出成品(postBuffer): 装配好的最终画面会被送到另一个最终的传送带上(显示设备的FrameBuffer)。然后屏幕在下一个VSync信号到来时,就会把这幅最终画面拉走并显示出来
总结:SurfaceFlinger是一个协调者,它收集所有UI层的输出,并利用GPU或HWC硬件,高效地将它们合成为最终要显示的一帧图像
三者如何协同工作?一个完整的故事
假设屏幕是60FPS,我们来看一帧画面是如何诞生的:
- VSYNC-app信号到来: 节拍器“滴答”一声。App听到后开始行动,从BufferQueue里申请一个空缓冲区(Buffer),开始绘制它的UI(执行
onDraw
) - App绘制完成: App在16.6ms内画完了,它将画好的缓冲区放回BufferQueue队列里,然后就可以休息,等待下一个节拍
- VSYNC-sf信号到来: 节拍器又一次“滴答”(可能是同一个信号,但在软件上是错开的)。SurfaceFlinger老师傅听到后开始工作
- SurfaceFlinger合成: 老师傅去所有应用的BufferQueue里取已经画好的缓冲区,连同系统UI的缓冲区一起,交给HWC或GPU去合成
- 显示: 合成好的最终图像被送到屏幕。紧接着,下一个VSync信号到来,屏幕将这个最终图像显示出来。同时,这个信号又开启了下一个周期,App开始绘制下一帧
整个过程如此循环往复,从而实现了流畅的视觉体验
简单概括:
- VSync 规定了 “何时” 做
- BufferQueue 提供了 “何物” (数据)并解决了数据交换问题
- SurfaceFlinger 负责 “如何做” (合成并显示)