从View 到 SurfaceFlinger

47 阅读7分钟

它们之间可以理解成“上半场:View/UI 框架负责画各自窗口的内容,下半场:SurfaceFlinger 负责把所有窗口的内容合成到屏幕”。

一、从 View 框架到 SurfaceFlinger 的“接力点”

【App 进程 / UI 线程侧:负责画出各自窗口的内容】
    Activity
      └─ Window(PhoneWindow)
             └─ DecorView (根 ViewGroup)
                   └─ ViewGroup / View 树
                         └─ onDraw(Canvas)
                               └─ Canvas → Skia → OpenGL ES (GPU)
                                     └─ 渲染到 Surface 的 buffer

    ViewRootImpl
        ├─ 驱动 measure/layout/draw
        ├─ 持有 Surface(写入端)
        └─ 通过 BufferQueue 作为 Producer 提交图像

    Surface / SurfaceView / GLSurfaceView
        └─ 每个对应一个 BufferQueue 的 Producer 端

    Choreographer / Handler
        └─ 按 VSync 节奏驱动 ViewRootImpl.doTraversal()(每帧 UI 渲染)

【System Server / 图形系统:负责合成所有 Surface,并送到屏幕】
    WindowManagerService (WMS)
        ├─ 管理所有 Window(位置、大小、Z 序、可见性)
        └─ 为每个 Window / SurfaceView 创建对应的 Layer/Surface 记录

    SurfaceFlinger
        ├─ 作为所有 Surface 的 Consumer 端
        ├─ 从 BufferQueue 取出每个 Surface 最新的 buffer
        ├─ 按 WMS 提供的窗口/Layer 信息进行合成
        └─ 通过 HWC / GPU 合成到最终帧缓冲

    Hardware Composer (HWC) / GPU / 显示驱动
        └─ 真正在硬件上完成 Layer 合成、输出到屏幕

关键链接点:

  • App 侧所有 View/UI 最终都画到 某个 Surface 的 buffer;

  • 而 SurfaceFlinger 是所有 Surface buffer 的 唯一消费者

二、App → Surface

先快速再梳理一下这些概念在 “App → Surface” 这半程中的作用:

Activity / Window / DecorView / View / ViewGroup

  • Activity 管理一个界面的生命周期,通过 setContentView() 告诉系统“我要显示这棵 View 树”。

  • Window (PhoneWindow) Activity 的窗口外壳,持有 DecorView。

  • DecorView Window 的根 ViewGroup,上面挂开发写布局的 View 树。

  • View / ViewGroup 完成 onMeasure() / onLayout() / onDraw(Canvas),只负责“画自己”,不知道屏幕、也不知道 SurfaceFlinger 的存在。

ViewRootImpl / WindowManager / WMS / Surface

  • WindowManager (WindowManagerImpl) App 侧“申请窗口”的入口,最终走到 WMS。

  • WindowManagerService (WMS) 系统侧统一管理所有 Window,并为每个 Window 创建一个在 SurfaceFlinger 那里的 Layer/Surface。

  • ViewRootImpl 连接 View 世界 ↔ Window/Surface 世界:

    • 持有根 View(DecorView);

    • 持有对应的 Surface(其实是 SurfaceFlinger 那个 Layer 的 Producer 句柄);

    • 驱动 measure/layout/draw,把结果画到 Surface 的 buffer。

Canvas / Skia / OpenGL ES

  • Canvas View 的绘图接口。

  • Skia Canvas 背后的实现,把 drawXxx 转成 2D 绘制命令(软件 or 硬件)。

  • OpenGL ES 在硬件加速时,Skia / HWUI 会通过 OpenGL ES 调用 GPU,把图像画到 Surface buffer。

Choreographer / Handler

  • Choreographer 接收 VSync 信号,在每帧里统一触发:输入→动画→ViewRootImpl.doTraversal()

  • Handler 在 UI 线程上排队执行 doTraversal()(即 measure/layout/draw)。

View 框架+Skia+OpenGL ES 的职责,就是“在每帧 VSync 时,把 Activity 对应窗口的那张 Surface buffer 画好”。 它们只负责产生每个窗口自己的画面。

三、中场交接:Surface / BufferQueue 是如何把帧交给 SurfaceFlinger 的?

每个 Surface 对应一条 BufferQueue

每个 Window(或 SurfaceView/GLSurfaceView)被 WMS/SurfaceFlinger 注册后,会对应一个 Layer,其底层有一对

  • BufferQueue Producer 生产端:给 App / ViewRootImpl / GL 使用;

  • BufferQueue Consumer 消费端:在 SurfaceFlinger 里。

    App 进程内: ViewRootImpl / SurfaceView / GLSurfaceView └─ 持有 Surface(Producer 端) └─ dequeueBuffer() → 拿到一个空 buffer └─ 在 buffer 上绘制(Canvas or OpenGL ES) └─ queueBuffer() → 把绘制好的帧入队

    System 进程内: SurfaceFlinger └─ 持有同一条 BufferQueue 的 Consumer 端 └─ acquireBuffer() → 在合成时刻取出最新帧 └─ releaseBuffer() → 用完后归还给队列

这就是上半场(App 绘制)和下半场(SurfaceFlinger 合成)精确的连接点。

一帧的交接时序(简化)

  1. UI 线程 / RenderThread 在 VSyncN 周期内完成 View 渲染 → GPU 画到 Surface 当前 back buffer;

  2. 渲染结束后调用 eglSwapBuffers()unlockCanvasAndPost()

    1. 实际上就是对 BufferQueue 的 queueBuffer()

    2. 这块 buffer 现在变成 “已渲染、待消费”状态。

  3. 下一个 SurfaceFlinger VSync(一般与屏幕刷新同步)到来时:

    1. SurfaceFlinger 从所有可见 Layer 的 BufferQueue 里 acquire 最新 buffer;

    2. 用这些 buffer 做合成,形成一张完整屏幕图像。

四、下半场:SurfaceFlinger 如何使用这些 Surface?

WMS 与 SurfaceFlinger 的关系

  • WMS 不负责画图,负责的是“窗口管理”:

    • 哪些窗口可见;

    • 它们的 Z 序(谁在上谁在下);

    • 每个窗口的位置、大小、透明度等。

  • SurfaceFlinger 是系统唯一的“合成器(Compositor)”:

    • 它维护一个 Layer 列表,每个 Layer 对应一个 Surface(即某个 Window / SurfaceView / Wallpaper 等);

    • 从各 Layer 的 BufferQueue 中取帧;

    • 根据 WMS 给出的 Layer 属性(位置、变换、Z序、透明度) 决定如何合成。

    [ WMS ] └─ 管理 Window └─ 为每个 Window 创建 Layer/Surface,并设置: - 位置/大小 - Z序 - 可见性/透明度 - transform 等

    [ SurfaceFlinger ] └─ 持有所有 Layer └─ 对每个 Layer: - 拿最新 buffer - 按 WMS 给的属性合成 └─ 输出一张整屏图像给显示系统

注意:SurfaceFlinger 不认识 View / Activity,它只看到一堆 Layer+buffer。

SurfaceFlinger 的合成过程

周期性(按 VSync)执行类似流程:

  1. 等待 VSync;

  2. 遍历所有可见 Layer:

    1. 对每个 Layer 的 BufferQueue 调用 acquireBuffer(),拿到最新帧;

    2. 如果没有新帧,就沿用上一帧(静止画面)。

  3. 根据 Layer 属性,将这些 buffer 按顺序“叠加”:

    1. 有的 Layer 可以直接由 HWC 叠加(Overlay);

    2. 有的 Layer 需要由 GPU 渲染完成(Client Composition)。

  4. 合成完成后,把结果交给显示控制器,在下一次刷新中输出到屏幕。

五、SurfaceFlinger的上层概念

下面按你列的关键概念,逐个说明它们 如何影响 SurfaceFlinger / 显示结果。

Activity / Window / DecorView / View / ViewGroup

  • 这些都只存在于 App 进程。

  • 它们的全部工作结果就是: “在本窗口对应的 Surface buffer 上画出这一帧的内容是什么”。

  • 对 SurfaceFlinger 来说,这些只是产生某个 Layer 的一张“bitmap - 位图”; 它完全不知道里面有多少 View、是什么布局。

总结:

这些概念决定“单个 Layer 的像素内容”,但不参与“多 Layer 合成”。

ViewRootImpl / WindowManager / WMS

  • ViewRootImpl

    • 负责把 View 树的绘制结果写入到 Surface buffer;

    • 它将 requestLayout()/invalidate() 变成按 VSync 对齐的帧输出;

    • 每次输出的一帧就是 SurfaceFlinger 将来用于合成的一个 buffer。

  • WindowManager (App 侧) 负责向 WMS 申请 / 更新 / 删除 Window,从而影响 SurfaceFlinger “有哪些 Layer”。

  • WMS (系统侧)

    • 决定:

      • 哪些 Window/Surface 在屏幕上;

      • 他们的 Z 序(如 Dialog 在上,Activity 在下);

    • 把这些信息同步给 SurfaceFlinger(Layer 属性)。

总结:

  • ViewRootImpl 决定 这一 Layer 每帧的内容;

  • WMS 决定 这个 Layer 怎么摆在屏幕上,与其他 Layer 的关系;

  • SurfaceFlinger 根据这些决策完成合成。

Surface / SurfaceView

  • Surface 是连接 App 绘制 ↔ SurfaceFlinger 合成的 唯一通道。

    • App 通过 Surface(Producer)往 BufferQueue 里 queue帧;

    • SurfaceFlinger 从 BufferQueue(Consumer)里取帧。

  • SurfaceView

    • 拥有独立的 Surface(即独立 Layer),直接被 SurfaceFlinger 看到;

    • 主窗口的 View 树还是画在 Activity 的 Window Surface 上;

    • SurfaceView 的内容是另一个 Layer,常用于:

      • 视频(MediaPlayer/MediaCodec 直接往 Surface 写帧);

      • 游戏/相机预览(GL 或自定义渲染线程绘制)。

对 SurfaceFlinger 的意义:

  • 一个普通 Activity 窗口:通常就是一个 Layer;

  • 加上一个 SurfaceView:则是 两个 Layer:

    • 背后是主窗口 Layer(UI 控件等);

    • 前面/中间是 SurfaceView 的 Layer(视频/游戏等);

  • SurfaceFlinger 合成时会把这两个 Layer 按 Z 序叠加。

Canvas / Skia / OpenGL ES

  • Canvas、Skia、OpenGL ES 只负责在 Surface 的某个 buffer 上画出当前帧像素; 画完这一帧,就通过 queueBuffer 提交给 SurfaceFlinger 消费。

  • 对 SurfaceFlinger 而言:

    • 不关心你是用 Canvas/Skia 画的,还是直接用 GL 画的;

    • 只关心:这个 Layer 在它合成时有没有一个最新的 buffer,以及这个 buffer 的格式/尺寸/变换。

Choreographer / Handler

  • Choreographer 收到的 VSync 实际来自 显示系统 / SurfaceFlinger / Display HAL,是显示管线统一的节奏信号。

    • App 侧用它来对齐自己的 UI 渲染帧;

    • SurfaceFlinger 侧也在 VSync 节奏下进行合成。

  • Handler 只是确保 doTraversal() 这些工作运行在 UI 线程。

这样 App 侧和 SurfaceFlinger 侧都跟 同一个(或严格同步的)VSync 源同步:

  • App 在 VSyncN 时画完自己这一帧(输出到 Surface buffer);

  • SurfaceFlinger 在后续某个 VSync(通常是 N 或 N+1)用到这个 buffer 进行合成;

  • 避免节奏错乱,减少撕裂和不规则卡顿。

六、一句话概括

Activity / View / Skia / OpenGL ES 只负责“在本窗口的 Surface 上按帧画图”;

WMS 决定每个 Surface 怎么摆;

SurfaceFlinger 在每个 VSync 周期,把所有 Surface 的最新帧按照 WMS 的布局规则合成为一张屏幕图像,再交给显示硬件显示。