Android SurfaceFlinger 笔记(二)

3 阅读6分钟

我们来深入SurfaceFlinger的内部,从源码视角拆解它每16ms的绘制循环。我将用一个更细粒度的流程图,配合关键数据结构和伪代码,帮你理解“总导演”是怎么一步步工作的。


🗺️ SurfaceFlinger主循环全流程(代码级视角)

SurfaceFlinger的主线程循环在SurfaceFlinger::onMessageReceived()中响应VSYNC信号触发,核心函数是SurfaceFlinger::doComposition()。整个流程可分解为四个阶段:

flowchart TD
    A[VSYNC到来] --> B[handleTransaction<br/>处理窗口事务]
    B --> C[handlePageFlip<br/>准备新缓冲区]
    C --> D[preComposition<br/>预合成计算]
    D --> E[rebuildLayerStacks<br/>重建可见图层列表]
    E --> F[setUpHWComposer<br/>配置HWC并决定合成方式]
    F --> G{是否需要GPU合成?}
    
    G -- 是 --> H[doComposeSurfaces<br/>用GPU合成所有图层]
    H --> I[postFramebuffer<br/>提交GPU合成的最终缓冲区]
    
    G -- 否 --> J[HWC直接合成]
    J --> I
    
    I --> K[更新各Layer的帧统计数据<br/>触发下一轮VSYNC]

下面我们逐一拆解。


1. handleTransaction() —— 处理窗口变化

入口SurfaceFlinger::handleTransaction()

  • 遍历所有Layer(图层),检查是否有未处理的事务(Transaction)。事务由WMS通过Binder发送给SurfaceFlinger,包含窗口位置、大小、Z序、透明度等变化。
  • 如果某个Layer有更新,调用Layer::doTransaction()更新其内部状态(如矩阵变换、裁剪区域)。
  • 记录哪些Layer发生了变化,以便后续只重绘受影响区域(damage tracking)。

关键数据结构

  • Layer:代表一个显示图层(一个应用窗口、状态栏等)。
  • Transaction:保存WMS发来的窗口属性变更。

2. handlePageFlip() —— 获取新缓冲区

入口SurfaceFlinger::handlePageFlip()

  • 遍历所有可见Layer,调用Layer::latchBuffer()
  • 内部机制:每个Layer内部有一个BufferQueue(生产者是应用)。latchBuffer()会尝试从BufferQueue中acquire一个已由应用绘制完成的GraphicBuffer(状态为QUEUED)。
    • 如果有新缓冲区,记录该Layer有更新,并将该缓冲区交给Layer持有。
    • 同时记录这个Layer的损坏区域(damage region),即自上一帧以来这个图层哪些部分发生了变化(应用可通过Surface.lockCanvas(Rect dirty)提供,或者全屏刷新)。
  • 这一步结束后,所有“有内容更新”的Layer都拿到了最新的图像缓冲区。

关键数据结构

  • BufferQueue:内部维护一个缓冲区队列,状态机:DEQUEUEDQUEUEDACQUIREDRELEASED
  • Region:用于记录每一层的损坏区域,用于后续合成优化。

3. preComposition() —— 预合成准备

  • 计算所有可见Layer的合成边界,生成最终的显示帧(DisplayFrame)。
  • 合并所有Layer的损坏区域,得到整个屏幕的脏区域(dirtyRegion)。如果脏区域为空,可以跳过合成直接进入下一轮(但一般都有)。
  • 调用invalidateHwcGeometry()通知HWC合成几何信息已变。

4. rebuildLayerStacks() —— 重建图层栈

  • 根据最新的Layer Z序和可见性,重建每个物理显示屏(可能多个)的图层列表。
  • 将Layer按照Z-order从低到高排序,形成最终的合成栈。

5. setUpHWComposer() —— 配置HWC并决定合成策略

这是合成决策的核心,位于SurfaceFlinger::setUpHWComposer()

  • 将当前所有可见Layer的信息(缓冲区句柄、变换矩阵、裁剪区域、透明度等)打包成HWC能理解的格式,传递给HAL层。
  • HWC会检查硬件能力,并返回每个图层的合成类型HWC2_COMPOSITION_CLIENTHWC2_COMPOSITION_DEVICE):
    • DEVICE:硬件可以直接合成(例如,图层是简单的RGBA、无复杂混合、数量不超过硬件overlay单元数)。
    • CLIENT:硬件无法合成,需要SurfaceFlinger用GPU合成。
  • SurfaceFlinger根据HWC的返回结果,决定最终的合成路径。

关键点

  • HWC模块通过hwc2_device_t接口与SurfaceFlinger通信。
  • HWC可以动态调整合成策略,例如当overlay单元不足时,自动将部分图层标记为CLIENT,交由GPU处理。

6. doComposeSurfaces() —— GPU合成(Client Composition)

如果某个或所有图层需要GPU合成,SurfaceFlinger会:

  • 选择一个目标图形缓冲区(可能是framebuffer或HWC提供的target buffer)。
  • 调用OpenGL ES驱动,对于每个需要GPU合成的Layer:
    • 绑定该Layer的纹理(GraphicBuffer通过EGLImage转为GL纹理)。
    • 应用该Layer的混合方程(src blend factor, dst blend factor)。
    • 绘制一个覆盖该Layer区域的四边形(带纹理坐标),将纹理内容绘制到目标缓冲区。
    • 注意绘制顺序:从Z-order最低的Layer开始向上绘制。
  • 最终,目标缓冲区中已经包含了所有CLIENT图层的合成结果。

性能优化

  • 如果多个Layer完全覆盖,可以提前裁剪避免绘制被遮挡的部分。
  • 只绘制损坏区域(incremental rendering)。

7. postFramebuffer() —— 提交显示

入口SurfaceFlinger::postFramebuffer()

  • 如果是GPU合成,此时目标缓冲区已准备好,调用eglSwapBuffers()将其提交给HWC(或直接送显)。如果是HWC合成,HWC已经自行将DEVICE图层和CLIENT图层(若有)合成为最终图像。
  • SurfaceFlinger调用HWC的presentDisplay()方法,通知HWC将最终图像输出到物理显示屏。
  • 等待硬件完成显示(通常异步,通过回调查看完成状态)。
  • 最后,释放已经合成过的图层缓冲区:对于GPU合成的图层,调用BufferQueue::releaseBuffer()将缓冲区状态置为FREE,允许应用重新使用;对于HWC合成的图层,由HWC负责释放。

🔁 循环结束,等待下一个VSYNC

SurfaceFlinger更新内部统计信息(如帧率、掉帧计数),并设置下一次VSYNC信号到来时继续执行合成。


📦 BufferQueue的细节交互

为了更完整,我们插入一个典型的BufferQueue生命周期(从应用绘制到显示):

  1. 应用dequeue:应用调用Surface.lockCanvas()BufferQueueProducer::dequeueBuffer(),获取一个空闲的GraphicBuffer。
  2. 应用绘制:CPU/GPU向缓冲区填充像素数据。
  3. 应用queue:应用调用unlockCanvasAndPost()BufferQueueProducer::queueBuffer(),缓冲区进入QUEUED状态,并通知SurfaceFlinger(通过onFrameAvailable回调)。
  4. SurfaceFlinger latch:在handlePageFlip阶段,调用BufferQueueConsumer::acquireBuffer()将缓冲区状态置为ACQUIRED。
  5. SurfaceFlinger合成:缓冲区被用作纹理或传给HWC。
  6. SurfaceFlinger release:合成完成后,调用BufferQueueConsumer::releaseBuffer(),缓冲区回到FREE状态。

🧠 核心数据结构与函数调用关系

阶段关键函数数据结构
事务处理SurfaceFlinger::handleTransaction()LayermCurrentState/mDrawingState
缓冲区获取SurfaceFlinger::handlePageFlip()BufferQueue + GraphicBuffer
合成决策SurfaceFlinger::setUpHWComposer()HWC2::Layer + HWC2::Display
GPU合成SurfaceFlinger::doComposeSurfaces()GLESRenderEngine + EGLImage
提交显示SurfaceFlinger::postFramebuffer()HWComposerpresentDisplay
缓冲区释放BufferQueueConsumer::releaseBuffer()BufferQueue 状态机

🧩 补充:多屏幕与虚拟显示

SurfaceFlinger不仅支持物理显示屏,还支持虚拟显示(如屏幕录制、投屏)。虚拟显示没有HWC,合成方式全部采用GPU合成,并将结果输出到内存中的GraphicBuffer,供MediaCodec编码。


⚡ 优化技巧(你知道为什么滑动这么流畅吗?)

  • VSYNC对齐:所有合成都发生在VSYNC时刻,避免画面撕裂(tearing)。
  • 提前合成(Triple Buffering):BufferQueue通常有3个缓冲区,允许应用和SurfaceFlinger并行工作,减少等待。
  • HWC重用:如果连续几帧的合成配置未变,HWC可以复用之前的合成结果,SurfaceFlinger甚至不需要重新合成。
  • 损坏区域追踪:只绘制变化的区域,减少GPU工作量。

📝 总结

SurfaceFlinger的绘制流程是一个由VSYNC驱动的、高度优化的状态机。它通过BufferQueue与应用解耦,利用HWC最大限度利用硬件,并在必要时回退到GPU合成,最终将所有图层合并成一帧送给屏幕。理解这个流程,就能明白Android图形系统为什么能同时保证高性能和低功耗。

如果你还想深挖某个子环节(例如HWC的合成策略细节、BufferQueue的同步机制、或者SurfaceFlinger如何与WindowManager通信),我们可以继续展开。