SurfaceFlinger 合成一帧的完整流程

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. 正确的调用层级

主调用树是:

text 复制代码
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 / OutputLayer Output 表示合成目标;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():

cpp 复制代码
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

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

text 复制代码
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

cpp 复制代码
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 等状态。

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

cpp 复制代码
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

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

cpp 复制代码
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 后完成统计和回调:

text 复制代码
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() 的执行顺序:

cpp 复制代码
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);
步骤 作用
preComposition LayerFE 处理帧前状态,汇总是否需再次 update
Output::prepare 按屏重建可见 OutputLayer,计算 coverage/dirty region,处理 uncache
offloadOutputs HWC 支持时允许多屏 present 并行
Output::present 各屏完成策略、GPU/HWC 合成、present 和 Layer 释放
postComposition 为没有在任何 Output 呈现的已 latch Layer 补齐 release-fence promise

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

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

cpp 复制代码
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 合成与上屏

7.1 真实执行顺序

text 复制代码
updateColorProfile
→ updateCompositionState
→ planComposition
→ writeCompositionState
→ setColorTransform
→ beginFrame
→ prepareFrame / prepareFrameAsync
→ devOptRepaintFlash
→ finishFrame
→ presentFrameAndReleaseLayers [可 offload]
→ renderCachedSets

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

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

OutputLayer::updateCompositionState() 写入:

cpp 复制代码
state.displayFrame = calculateOutputDisplayFrame();
state.sourceCrop = calculateOutputSourceCrop(internalDisplayRotationFlags);
state.bufferTransform = calculateOutputRelativeBufferTransform(...);

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

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

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

text 复制代码
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

cpp 复制代码
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 如何串起整帧

text 复制代码
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 fence App/GPU 生产者 消费者不在 buffer 写完前读取
client target acquire fence RenderEngine HWC 不在 GPU 合成完成前扫描 client target
present fence HWC 描述 display 呈现完成时序,也用于帧时序模型
layer release fence HWC,必要时与 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 生命周期。

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui