SurfaceFlinger 合成一帧的完整流程

0 阅读9分钟

SurfaceFlinger 合成一帧的完整流程

本文以 Android 16 的物理屏常规路径为主线:从 Scheduler 收到一次帧信号,到 SurfaceFlinger 提交状态、锁定 buffer、完成 GPU/HWC 分工、present,再到 fence 和回调收尾。虚拟屏、多屏 offload、合成策略预测等分支单独说明,不打断主线。


目录

  1. 正确的调用层级
  2. 关键对象和数据
  3. 阶段 A:Scheduler 发起一帧
  4. 阶段 B:commit 提交本帧状态
  5. 阶段 C:composite 组装合成输入
  6. 阶段 D:CompositionEngine 协调 Output
  7. 阶段 E:单屏 Output 合成与上屏
  8. fence 如何串起整帧
  9. 帧结束、回调与下一帧
  10. 重要分支与常见误解

1. 正确的调用层级

01-frame-overview.png

主调用树是:

Scheduler::onFrameSignal
├─ SurfaceFlinger::commit
│  └─ updateLayerSnapshots
│     ├─ collect / addLayers / flush / applyTransactions
│     ├─ LayerHierarchyBuilder::update
│     ├─ LayerSnapshotBuilder::update
│     └─ Layer::latchBufferImpl
└─ SurfaceFlinger::composite
   ├─ 组装 CompositionRefreshArgs
   └─ CompositionEngine::present
      ├─ preComposition
      ├─ Output::prepare                 // 每块屏建立可见 OutputLayer
      ├─ Output::present                 // 每块屏合成、present、释放
      └─ postComposition

**关键纠偏:**不要把 commit + composite + present 当成三个并列阶段。CompositionEngine::present()SurfaceFlinger::composite() 内部的主要执行步骤,Output::present() 又是它的单屏子流程。

从职责上看,一帧只有两个顶层阶段:

  • commit:把本帧应使用的 Layer 状态和 buffer 定下来
  • composite:把已确定的状态变成各屏的合成任务,完成 validate → GPU/HWC 合成 → present → release

2. 关键对象和数据

对象本帧职责
Scheduler / FrameTargeter建立帧目标,调用 commit() / composite(),最后执行 endFrame()
SurfaceFlinger刷新事务、维护前端状态、锁定 buffer,并组装 CompositionRefreshArgs
RequestedLayerState保存服务端收到的请求状态、transaction 字段和 changes
LayerHierarchy根据父子、relative-Z 和 mirror 关系提供 Z 序遍历
LayerSnapshot保存特定遍历路径下的几何、可见性、buffer、色彩和效果等派生状态
LayerFE向 CompositionEngine 暴露快照,并生成 GPU 所需的 LayerSettings
Output / OutputLayerOutput 表示合成目标;OutputLayer 表示 Layer 在该输出上的状态
Display将物理屏的 Output present 操作落到 HWComposer
RenderEngine把 CLIENT 类型的 Layer 绘制到 client target
HWComposer封装 Composer HAL 的 validate、present 和 fence 获取

LayerSnapshot 不是一个全局唯一的 Layer 副本。镜像或相对层级会让同一 Layer 沿不同 TraversalPath 出现,因此快照是“Layer + 遍历路径”下的合成状态。


3. 阶段 A:Scheduler 发起一帧

入口为 Scheduler::onFrameSignal()

const FrameTargeter::BeginFrameArgs beginFrameArgs = {
        .frameBeginTime = mClock->now(),
        .vsyncId = vsyncId,
        .expectedVsyncTime = expectedVsyncTime,
        .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
        .hwcMinWorkDuration = getCurrentVsyncConfigs().hwcMinWorkDuration,
};

pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr);
if (!compositor.commit(pacesetterPtr->displayId, targets)) return;
const auto results = compositor.composite(pacesetterPtr->displayId, targeters);
for (const auto& [id, targeter] : targeters) targeter->endFrame(*results.get(id));
  1. 先为 pacesetter 屏建立 FrameTarget,确定 expectedPresentTime、截止时间和前一帧 fence 状态。
  2. 再为可 present 的 follower 屏建立目标。follower 可因 backpressure 被暂时排除。
  3. 严格按 commit → composite → endFrame 执行。commit() 返回 false 时,本次不进入合成。

onFrameSignal 接到的是 Scheduler 分发后的帧信号;其时钟源可由 VSyncSchedule 的当前配置决定,不应简化成“始终直接接收面板硬件 VSYNC”。


4. 阶段 B:commit 提交本帧状态

4.1 先决定这次是否值得合成

SurfaceFlinger::commit() 先处理显示模式切换、HWC backpressure 等情况,再从 mMustComposite、前端状态变化、新 buffer latch 和 display transaction 等来源累积 mustComposite

didMissFrame() 主要用于统计和时序判断,并不等价于“只要 miss 就立即 return false”。

4.2 事务从 Binder 队列进入 RequestedLayerState

02-transaction-flush.png

新建 Layer 必须在 flush 前入册,所以完整顺序是:

SurfaceFlinger::setTransactionState
  → TransactionHandler::queueTransaction          // Binder 线程入无锁队列

SurfaceFlinger::updateLayerSnapshots
  → collectTransactions                           // 按 applyToken 归入 pending queues
  → LayerLifecycleManager::addLayers              // 先注册本批新 Layer
  → flushTransactions                             // readiness / barrier / present time
  → LayerLifecycleManager::applyTransactions
       → RequestedLayerState::merge               // 合并 layer_state_t,设置 changes

applyToken 保证同一 token 队列内顺序;barrier、buffer readiness 和期望呈现时间可以让队头事务继续等待。merge() 同时保留原始 what 位和更粗粒度的 RequestedLayerState::Changes,供增量更新使用。

4.3 更新层级图,再生成 LayerSnapshot

03-layer-snapshot.png

mLayerHierarchyBuilder.update(mLayerLifecycleManager);

frontend::LayerSnapshotBuilder::Args args{
        .root = mLayerHierarchyBuilder.getHierarchy(),
        .layerLifecycleManager = mLayerLifecycleManager,
        .displays = mFrontEndDisplayInfos,
        // metadata / blur / shadow / generic metadata ...
};
mLayerSnapshotBuilder.update(args);
  • LayerHierarchyBuilder根据 parentId / relativeParentId / mirror 边更新场景图,并以 Z 序遍历。
  • LayerSnapshotBuilder为遍历路径创建或增量更新快照,继承父层的 alpha、transform、crop、metadata 等状态。

几何计算的核心关系如下:

snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
snapshot.geomLayerBounds = requested.externalTexture ? snapshot.bufferSize.toFloatRect()
                                                    : parentBounds;
snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop);
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);

此时产出的是后端合成可消费的本帧快照,不是已经画好的像素。

4.4 锁定本帧 buffer

04-buffer-latch.png

updateLayerSnapshots() 随后对已准备好帧或需释放 buffer 的 Layer 调用 Layer::latchBufferImpl()

if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) continue;
layer->latchBufferImpl(recomputeVisibleRegions, latchTime,
                       expectedPresentTimeNs, bgColorOnly);

latchBufferImpl() 依次处理 sideband stream;在策略要求时检查 acquire fence;用 updateTexImage() 记录 latch/acquire 时序和 on-commit callback;再由 gatherBufferInfo() 收集 buffer、fence、frame number、buffer transform 和 dataspace。buffer 尺寸、crop 或 transform 变化还会要求重算 visible region。

例外:Layer::fenceHasSignaled()enableLatchUnsignaledConfig 不是 Disabled 时会允许锁定未 signal 的 acquire fence。因此“acquire fence 未 signal 必然不 latch”不是 Android 16 所有配置下的绝对规则。

4.5 commit 的输出

commit 结束时,RequestedLayerState / hierarchy / snapshots 已更新,可用新 buffer 已 latch,on-commit 回调可发送,刷新率选择可使用新状态。其返回值决定 Scheduler 是否继续调用 composite()


5. 阶段 C:composite 组装合成输入

SurfaceFlinger::composite() 组装输入、执行 CompositionEngine,并在 present 后完成统计和回调:

1. 填充 CompositionRefreshArgs
   - color setting / frame interval / scheduled frame time
   - updatingGeometryThisFrame / updatingOutputGeometryThisFrame
   - frameTargets / bufferIdsToUncache
2. addOutputsToRefreshArgs
   - 物理屏通常进主线程
   - 符合条件的 GPU 虚拟屏可 offload
3. addLayerSnapshotsToCompositionArgs
4. prepareLayersForComposition
   - Layer::onPreComposition
   - 为 release fence 建立 future/promise
5. CompositionEngine::present
6. onCompositionPresented / callbacks / TimeStats / power hint

CompositionRefreshArgs 是前端到合成引擎的帧级边界:args.layers 是 LayerFE 序列,args.outputs 是本帧要处理的合成目标。


6. 阶段 D:CompositionEngine 协调 Output

CompositionEngine::present() 的执行顺序:

preComposition(args);
LayerFESet latchedLayers;
for (const auto& output : args.outputs) output->prepare(args, latchedLayers);

offloadOutputs(args.outputs);
for (const auto& output : args.outputs) presentFutures.push_back(output->present(args));
for (auto& future : presentFutures) future.get();
postComposition(args);
步骤作用
preCompositionLayerFE 处理帧前状态,汇总是否需再次 update
Output::prepare按屏重建可见 OutputLayer,计算 coverage/dirty region,处理 uncache
offloadOutputsHWC 支持时允许多屏 present 并行
Output::present各屏完成策略、GPU/HWC 合成、present 和 Layer 释放
postComposition为没有在任何 Output 呈现的已 latch Layer 补齐 release-fence promise

6.1 Output::prepare:从 LayerFE 到可见 OutputLayer

05-visible-region.png

rebuildLayerStacks() 从前到后遍历层(对已按 Z 序的 args.layers 使用 reversed()),累积上方层 coverage:

coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion);
coverage.aboveCoveredLayers.orSelf(visibleRegion);
visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);

if (layerFEState->contentDirty) {
    dirty = visibleRegion | oldVisibleRegion;
} else {
    const Region newExposed = visibleRegion - coveredRegion;
    const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
    dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed);
}
coverage.aboveOpaqueLayers.orSelf(opaqueRegion);
  • visibleRegion:本层足迹减去上方不透明层;被半透明层覆盖的区域仍可见。
  • coveredRegion:被上方所有可见区域覆盖的部分,包含半透明覆盖。
  • opaqueRegion:在可安全判定时,本层完全遮挡下方的区域。
  • dirtyRegion:内容变化、新暴露或几何变化需重画的区域。

7. 阶段 E:单屏 Output 合成与上屏

06-output-present.png

7.1 真实执行顺序

updateColorProfile
→ updateCompositionState
→ planComposition
→ writeCompositionState
→ setColorTransform
→ beginFrame
→ prepareFrame / prepareFrameAsync
→ devOptRepaintFlash
→ finishFrame
→ presentFrameAndReleaseLayers [可 offload]
→ renderCachedSets

beginFrame() 设置 mMustRecompose。即使不需重画 client target,物理屏/HWC 也可能需继续状态机,所以不能把“脏区为空”直接等同于整个 present 跳过。

7.2 计算每层在该屏上的状态

OutputLayer::updateCompositionState() 写入:

state.displayFrame = calculateOutputDisplayFrame();
state.sourceCrop = calculateOutputSourceCrop(internalDisplayRotationFlags);
state.bufferTransform = calculateOutputRelativeBufferTransform(...);

同时计算 dataspace、HDR/SDR dimming、white point、LUT,并根据安全显示、无效 transform、色域支持、调试开关和 LayerFE 状态标记 forceClientComposition

7.3 先写 HWC 请求,再 validate 定最终分工

07-hwc-state.png

writeCompositionState() 遍历 Z 序 OutputLayer,把 displayFrame / sourceCrop / bufferTransform / z、blend/alpha/dataspace/HDR/LUT、buffer/fence 和请求的 composition type 翻译成 HWC2::Layer 命令。这一步只是填写 Composer 命令,还没有最终上屏。

prepareFrame() 通过 Display::chooseCompositionStrategy() 进入 HWComposer::getDeviceCompositionChanges()。HWC validate 可要求某些层从 DEVICE 改为 CLIENT,applyCompositionStrategy() 把最终结果写回 OutputLayer。

某种效果是否必然走 GPU 取决于 Layer 状态、HWC 能力和 validate 结果。不能把“有圆角”简单写成“必然 CLIENT”;当前前端显式强制 CLIENT 的效果包括 shadow、stretch、edge extension、border/box shadow 等,还有输出/HWC 限制。

7.4 CLIENT 层由 RenderEngine 合成为 client target

08-gpu-hwc.png

dequeueRenderBuffer
→ composeSurfaces
   ├─ generateClientCompositionDisplaySettings
   ├─ generateClientCompositionRequests
   │  └─ LayerFE::prepareClientComposition
   └─ RenderEngine::drawLayers(..., clientTarget, acquireFence)
→ RenderSurface::queueBuffer(GPU-fence)

composeSurfaces()usesClientComposition == false 时直接返回;有 CLIENT 层时,RenderEngine 按 Z 序画到一块 client target。该目标缓冲对 HWC 是一个整体输入,GPU 完成 fence 随 queueBuffer() 成为 HWC 使用它之前的等待条件。

7.5 HWC present,再设置 release fence

hwc.presentAndGetReleaseFences(halDisplayId, earliestPresentTime);
fences.presentFence = hwc.getPresentFence(halDisplayId);
for (const auto* layer : getOutputLayersOrderedByZ()) {
    fences.layerFences.emplace(hwcLayer,
            hwc.getLayerReleaseFence(halDisplayId, hwcLayer));
}

presentFrameAndReleaseLayers() 先取 HWC layer release fence。如果本帧使用 client composition,还会把相关 Layer 的释放条件与 client-target acquire fence 合并,然后通过 LayerFE::setReleaseFence() 完成之前建立的 promise。


8. fence 如何串起整帧

App/producer 写 buffer
  └─ acquire fence ──→ Layer latch / HWC 或 GPU 读取前等待

RenderEngine 写 client target
  └─ GPU completion fence ──→ client target 的 acquire fence

HWC present
  ├─ present fence ──→ FrameTimeline / Scheduler 时序
  └─ per-layer release fence ──→ 生产者何时可复用旧 buffer
fence谁生产保护什么
buffer acquire fenceApp/GPU 生产者消费者不在 buffer 写完前读取
client target acquire fenceRenderEngineHWC 不在 GPU 合成完成前扫描 client target
present fenceHWC描述 display 呈现完成时序,也用于帧时序模型
layer release fenceHWC,必要时与 client-target fence 合并上一个 Layer buffer 何时不再被消费

fence 是可传递、可合并的同步原语。一个 fence 已经被交给下游,不代表它此刻已 signal。


9. 帧结束、回调与下一帧

SurfaceFlinger::onCompositionPresented() 在提交 present 后:

  1. HWComposer 取各物理屏 present fence,设置到 FrameTargeter
  2. 将 pacesetter present fence 交给 transaction callback 和 FrameTimeline。
  3. 更新 Layer 的 presented/jank 信息,调用 releasePendingBuffer()
  4. 发送非 on-commit-only 的 transaction completed callback。
  5. 将 present fence 送回 Scheduler,供 VSync 模型、backpressure 和统计使用。

回到 Scheduler::onFrameSignal() 后,FrameTargeter::endFrame() 记录本帧 CompositeResult。如果 CompositionEngine 还需再 update,或期间到达了新 transaction/buffer,Scheduler 会安排下一帧。


10. 重要分支与常见误解

10.1 合成策略预测

Output::present() 可走 prepareFrameAsync():用上一帧 HWC changes 预测 GPU 工作,同时异步 validate。预测未命中时应用新 changes,利用已 dequeue 的 buffer 重做正确合成。不应把该路径写成“一般永远不开”。

10.2 present offload 和多屏

CompositionEngine::offloadOutputs()Output::presentFrameAndReleaseLayersAsync() 可以把支持 multi-threaded present 的 HWC 调用移出主线程。符合条件的 GPU 虚拟屏还可由 SurfaceFlinger::offloadGpuCompositedDisplays() 在后台 CompositionEngine 处理。

10.3 GPU 和 HWC 不是二选一

  • 全 DEVICE:层都交给 HWC。
  • 混合:CLIENT 层由 GPU 合成为 client target,再与 DEVICE 层一起交给 HWC。
  • 以 CLIENT 为主:大部分或全部层进 client target,HWC 仍负责 display present。

10.4 不是每个帧信号都产生新画面

commit() 可因 mode-set pending、backpressure、没有必要合成的变化或 boot 阶段返回 false。即使进入 composite(),某个 Output 也可能复用 client composition 或不重画 GPU 目标。



总结

SurfaceFlinger 合成一帧的核心是:Scheduler 给出时序目标;commit 将 transaction、层级、快照和 buffer 收敛为本帧状态;composite 把它们分发给各 Output;HWC validate 决定 GPU/HWC 分工;RenderEngine 仅生成需要的 client target;HWC 完成 present;最后用 present/release fence 关闭本帧时序和 buffer 生命周期。