深入理解Android中的SurfaceFlinger

这是一篇关于 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
2
[你的 App (OpenGL)]  -->  [BufferQueue]  -->  [SurfaceFlinger]  -->  [屏幕 (Display)]
(eglSwapBuffers) (入队) (合成 & 输送)

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。
  • TextViewButton 所在的 UI 窗口也是一个 Layer。
  • 状态栏是一个 Layer。
  • SurfaceFlinger 持有这些 Layer 的列表,并根据 Z-order(谁在上面谁在下面)来决定遮挡关系。

C. HWC (Hardware Composer) —— 性能优化的关键

这是 Android 图形流畅的关键。
合成图层有两种方式:

  1. GLES 合成 (Client Composition): SurfaceFlinger 使用 GPU (OpenGL) 把各个图层画到一张大图上。这很费电,也占用 GPU 资源。
  2. 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 之后发生了什么:

  1. 应用层 (Render Thread):

    • 你调用 eglSwapBuffers()
    • EGL 驱动将当前显存中的 FrameBuffer 放入 BufferQueue
    • BufferQueue 通知 SurfaceFlinger:“有新货了”。
  2. 系统层 (SurfaceFlinger):

    • 等待下一个 VSYNC 信号到来。
    • 信号一来,SurfaceFlinger 锁定当前所有 Layer 的状态。
    • 它从你的 BufferQueue 中取出这个 Buffer (Acquire Buffer)。
  3. 合成决策:

    • SF 看着你的 Surface 和上面的状态栏。
    • 它告诉 HWC:“把 App 的 Buffer 放在底层,状态栏的 Buffer 盖在上面”。
  4. 显示:

    • HWC 读取内存中的数据,合成电信号,发送给显示屏(LCD/OLED)。
    • Buffer 被显示后,SF 会释放它,把它还给 BufferQueue。
  5. 回收:

    • 你的 App 下次 eglSwapBuffers 时,或者是 dequeueBuffer 时,就能复用这块已经显示过的内存(这就是双缓冲/三缓冲的循环)。

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 服务),但你的几个操作直接影响它:

  1. SurfaceView vs TextureView:

    • SurfaceView: 拥有独立的 Surface。SurfaceFlinger 直接通过 HWC 合成它。性能最高,但无法做 View 动画(平移、缩放),因为它不在 View 树里,是一个独立的图层挖了个洞。
    • TextureView: 没有独立的 Surface。它劫持了 OpenGL 的渲染结果,变成一个普通的 OpenGL 纹理,交给 App 的 UI 线程去绘制。性能稍差,但可以像普通 View 一样做动画
  2. surfaceDestroyed 的真相:

    • 当这个回调触发时,意味着 SurfaceFlinger 里的这个 Layer 被移除了。
    • BufferQueue 断开了。
    • 如果你还在往里 queueBuffer (swapBuffers),就会报错或丢失。

总结

  • SurfaceFlinger 是 Android 的画师,负责把所有 App 的画稿拼在一起。
  • BufferQueue 是传输画稿的传送带。
  • HWC 是画师的强力助手(硬件加速),能不用 GPU 就不拥 GPU。
  • 你在 Demo 中做的 eglSwapBuffers,本质上就是向 SurfaceFlinger 提交作业。