我们来深入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:内部维护一个缓冲区队列,状态机:DEQUEUED→QUEUED→ACQUIRED→RELEASED。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_CLIENT或HWC2_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开始向上绘制。
- 绑定该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生命周期(从应用绘制到显示):
- 应用dequeue:应用调用
Surface.lockCanvas()→BufferQueueProducer::dequeueBuffer(),获取一个空闲的GraphicBuffer。 - 应用绘制:CPU/GPU向缓冲区填充像素数据。
- 应用queue:应用调用
unlockCanvasAndPost()→BufferQueueProducer::queueBuffer(),缓冲区进入QUEUED状态,并通知SurfaceFlinger(通过onFrameAvailable回调)。 - SurfaceFlinger latch:在handlePageFlip阶段,调用
BufferQueueConsumer::acquireBuffer()将缓冲区状态置为ACQUIRED。 - SurfaceFlinger合成:缓冲区被用作纹理或传给HWC。
- SurfaceFlinger release:合成完成后,调用
BufferQueueConsumer::releaseBuffer(),缓冲区回到FREE状态。
🧠 核心数据结构与函数调用关系
| 阶段 | 关键函数 | 数据结构 |
|---|---|---|
| 事务处理 | SurfaceFlinger::handleTransaction() | Layer 的 mCurrentState/mDrawingState |
| 缓冲区获取 | SurfaceFlinger::handlePageFlip() | BufferQueue + GraphicBuffer |
| 合成决策 | SurfaceFlinger::setUpHWComposer() | HWC2::Layer + HWC2::Display |
| GPU合成 | SurfaceFlinger::doComposeSurfaces() | GLESRenderEngine + EGLImage |
| 提交显示 | SurfaceFlinger::postFramebuffer() | HWComposer 的 presentDisplay |
| 缓冲区释放 | 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通信),我们可以继续展开。