这是一篇关于 Android 图形系统核心——SurfaceFlinger 的深度解析。
既然你已经掌握了如何使用 EGLContext 和独立线程进行渲染,那么理解 SurfaceFlinger 将帮助你打通从 swapBuffers 到屏幕显示的“最后一公里”。
1. 什么是 SurfaceFlinger?
用一句话概括:SurfaceFlinger 是 Android 系统级的“合成器” (Compositor)。
你的 App(包括状态栏、导航栏、壁纸)各自都在画画,每个人都有一块自己的画布(Surface)。但是手机屏幕只有一个。
SurfaceFlinger 的工作就是:把所有 App 绘制产生的“图层”,按照位置、层级(Z-order)、透明度,合成(Composite)为一张最终的图像,然后交给屏幕显示。
2. 核心架构:生产者与消费者模型
理解 SurfaceFlinger 最好的方式是通过 BufferQueue(缓冲队列)。
- 生产者 (Producer): 你的 App(或者说你的 OpenGL 线程)。
- 当你调用
eglSwapBuffers时,你产生了一帧数据。
- 当你调用
- 缓冲队列 (BufferQueue): 连接 App 和 SurfaceFlinger 的管道。
- 消费者 (Consumer): SurfaceFlinger。
- 它从队列里取出你画好的 Buffer,拿去合成。
1 | [你的 App (OpenGL)] --> [BufferQueue] --> [SurfaceFlinger] --> [屏幕 (Display)] |
3. SurfaceFlinger 的三大核心机制
A. VSYNC (垂直同步信号)
SurfaceFlinger 不是随叫随到的,它是跟着心跳走的。这个心跳就是 VSYNC。
- 屏幕通常以 60Hz(或 90/120Hz)刷新。
- 每隔 16.6ms,硬件会发出一个 VSYNC 信号。
- SurfaceFlinger 收到信号后,才会醒过来工作:“嘿,各个 App 们,把你们最新的 Buffer 给我,我要开始合成了!”
- Choreographer: App 端接收 VSYNC 的组件。它告诉你的 App:“快点画,下一帧合成马上要开始了”。
B. Layer (图层)
在 SurfaceFlinger 内部,每一个应用窗口对应一个 Layer。
- 你的
SurfaceView是一个 Layer。 -
TextView、Button所在的 UI 窗口也是一个 Layer。 - 状态栏是一个 Layer。
- SurfaceFlinger 持有这些 Layer 的列表,并根据 Z-order(谁在上面谁在下面)来决定遮挡关系。
C. HWC (Hardware Composer) —— 性能优化的关键
这是 Android 图形流畅的关键。
合成图层有两种方式:
- GLES 合成 (Client Composition): SurfaceFlinger 使用 GPU (OpenGL) 把各个图层画到一张大图上。这很费电,也占用 GPU 资源。
- HWC 合成 (Device Composition): 现代手机的处理芯片(SoC)里有一个专门的 2D 硬件合成单元(Display Subsystem)。它能非常高效地把几个 Buffer 叠加在一起,完全不需要 GPU 参与。
工作流程:
SurfaceFlinger 会问 HWC:“这几个图层你能处理吗?”
- **HWC 说能 (Overlay)**:SF 直接把 Buffer 句柄丢给 HWC,SF 自己这就休息了。(最省电,最快)
- HWC 说不能(比如图层做了复杂的 3D 变换,或者图层超过了硬件支持数量):SF 就唤醒 GPU,用 OpenGL 把这些图层画成一张图,再交给 HWC。
4. 结合你的 Demo:数据是如何流转的?
让我们回到你写的 OpenGL Demo,看看 swapBuffers 之后发生了什么:
应用层 (Render Thread):
- 你调用
eglSwapBuffers()。 - EGL 驱动将当前显存中的 FrameBuffer 放入 BufferQueue。
- BufferQueue 通知 SurfaceFlinger:“有新货了”。
- 你调用
系统层 (SurfaceFlinger):
- 等待下一个 VSYNC 信号到来。
- 信号一来,SurfaceFlinger 锁定当前所有 Layer 的状态。
- 它从你的 BufferQueue 中取出这个 Buffer (Acquire Buffer)。
合成决策:
- SF 看着你的 Surface 和上面的状态栏。
- 它告诉 HWC:“把 App 的 Buffer 放在底层,状态栏的 Buffer 盖在上面”。
显示:
- HWC 读取内存中的数据,合成电信号,发送给显示屏(LCD/OLED)。
- Buffer 被显示后,SF 会释放它,把它还给 BufferQueue。
回收:
- 你的 App 下次
eglSwapBuffers时,或者是dequeueBuffer时,就能复用这块已经显示过的内存(这就是双缓冲/三缓冲的循环)。
- 你的 App 下次
5. 为什么会有“撕裂 (Tearing)”和“卡顿 (Jank)”?
- 撕裂 (Tearing): 如果没有 VSYNC,SurfaceFlinger 在屏幕扫描到一半时突然换了一张新图,屏幕上半部分是旧帧,下半部分是新帧。Android 4.1 之后通过 Project Butter 强制开启 VSYNC 解决了这个问题。
- 卡顿 (Jank):
- 掉帧: 你的
RenderThread绘制太慢,超过了 16.6ms。 - VSYNC 到了,SF 问你要 Buffer,结果 BufferQueue 是空的(你还没画完)。
- SF 只能拿上一帧的画面继续显示(画面静止不动)。
- 用户感觉到“卡了一下”。
- 掉帧: 你的
6. 开发者视角的 SurfaceFlinger
作为开发者,你通常接触不到 SF 的代码(它是 C++ 写的 Native 服务),但你的几个操作直接影响它:
SurfaceView vs TextureView:
- SurfaceView: 拥有独立的 Surface。SurfaceFlinger 直接通过 HWC 合成它。性能最高,但无法做 View 动画(平移、缩放),因为它不在 View 树里,是一个独立的图层挖了个洞。
- TextureView: 没有独立的 Surface。它劫持了 OpenGL 的渲染结果,变成一个普通的 OpenGL 纹理,交给 App 的 UI 线程去绘制。性能稍差,但可以像普通 View 一样做动画。
surfaceDestroyed的真相:- 当这个回调触发时,意味着 SurfaceFlinger 里的这个 Layer 被移除了。
- BufferQueue 断开了。
- 如果你还在往里
queueBuffer(swapBuffers),就会报错或丢失。
总结
- SurfaceFlinger 是 Android 的画师,负责把所有 App 的画稿拼在一起。
- BufferQueue 是传输画稿的传送带。
- HWC 是画师的强力助手(硬件加速),能不用 GPU 就不拥 GPU。
- 你在 Demo 中做的
eglSwapBuffers,本质上就是向 SurfaceFlinger 提交作业。