It's our wits that make us men.

SurfaceFlinger 合成流程详解:含圆角小窗的混合合成

Posted on By pengmengxing

场景:屏幕上有 5 个 Layer,从低到高依次为:

Z 序 Layer 合成方式
壁纸 DEVICE(HWC)
桌面 DEVICE(HWC)
小窗(厂商实现,setCornerRadius() 圆角) CLIENT(GPU)
状态栏 DEVICE(HWC)
导航栏 DEVICE(HWC)

核心结论:圆角不通过 forceClientComposition 传播,只影响小窗自身; 壁纸和桌面不被拉入 GPU 合成,保持 DEVICE。


与 BackgroundBlur 场景的本质区别

维度 BackgroundBlur 场景 圆角场景
触发入口 findLayerRequestingBackgroundComposition() hasRoundedCorners()
强制路径 updateCompositionStateforceClientComposition 参数向下传播 isClientCompositionForced() 单层判断
影响范围 模糊层及其以下所有层全部 CLIENT 仅圆角层自身 CLIENT
原因 模糊需要采样下方所有层的像素作为 blurInput GPU 只需 clipRRect 裁剪这一层的内容

第一步:圆角属性如何产生影响

setCornerRadius() 调用后,LayerSnapshot 更新两个字段(LayerSnapshot.cpp:519,531):

// LayerSnapshot.cpp:519 — forceClientComposition 的设置条件
forceClientComposition = shadowSettings.length > 0   // 阴影
                      || stretchEffect.hasEffect()    // Overscroll 拉伸
                      || edgeExtensionEffect.hasEffect()
                      || borderSettings.strokeWidth > 0;
// ↑ 注意:圆角不在这里!LayerSnapshot 层面不强制 CLIENT

// LayerSnapshot.cpp:531 — 圆角让层变成非不透明
isOpaque = contentOpaque
        && !roundedCorner.hasRoundedCorners()   // ← 有圆角 → isOpaque = false
        && color.a == 1.f;

LayerFE::hasRoundedCorners()LayerFE.cpp:377):

bool LayerFE::hasRoundedCorners() const {
    return mSnapshot->roundedCorner.hasRoundedCorners();
    // 即 roundedCorner.radius.x > 0 && radius.y > 0
}

第二步:OutputLayer 判断 forceClientComposition

OutputLayer::updateCompositionStateOutputLayer.cpp:346):

void OutputLayer::updateCompositionState(
        bool includeGeometry, bool forceClientComposition, ...) {

    if (includeGeometry) {
        state.forceClientComposition = false;  // 每帧先清零

        // 触发强制的条件(圆角不在其中):
        if ((layerFEState->isSecure && !outputState.isSecure) ||
            (state.bufferTransform & ui::Transform::ROT_INVALID)) {
            state.forceClientComposition = true;
        }
    }

    // 本场景:
    //   没有 blur 层 → forceClientComposition 参数 = false
    //   layerFEState->forceClientComposition = false(圆角不在 LayerSnapshot 条件里)
    if (layerFEState->forceClientComposition
        || !profile.isDataspaceSupported(state.dataspace)
        || forceClientComposition) {
        state.forceClientComposition = true;
    }
    // 结论:state.forceClientComposition = false(圆角走另一条路)
}

第三步:写入 HWC 前的强制判断(核心)

OutputLayer::isClientCompositionForcedOutputLayer.cpp:979):

bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
    return getState().forceClientComposition          // = false(上面分析)
        || (!isPeekingThrough                         // = true(正常渲染路径)
            && getLayerFE().hasRoundedCorners());      // = true(小窗有圆角)
    // 最终返回 true ← 圆角在这里触发强制 CLIENT
}

OutputLayer::writeCompositionTypeToHWCOutputLayer.cpp:884):

void OutputLayer::writeCompositionTypeToHWC(
        HWC2::Layer* hwcLayer,
        Composition requestedCompositionType,   // 初始 = DEVICE(HWC 分配的)
        bool isPeekingThrough, bool skipLayer) {

    if (isClientCompositionForced(isPeekingThrough)) {   // = true
        requestedCompositionType = Composition::CLIENT;  // 覆盖为 CLIENT
    }

    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
        outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
        hwcLayer->setCompositionType(requestedCompositionType);
        // ↑ 通过 HIDL/AIDL 告知 HWC:这一层必须是 CLIENT
    }
}

关键state.forceClientComposition=falseisClientCompositionForced()=true, 圆角走的是独立检测路径,不通过 updateCompositionState 传播,只影响自身


第四步:DEVICE 层的 Buffer 如何送给 HWC

壁纸/桌面/状态栏/导航栏走 writeOutputIndependentPerFrameStateToHWCOutputLayer.cpp:722):

void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
        HWC2::Layer* hwcLayer,
        const LayerFECompositionState& outputIndependentState,
        Composition compositionType, bool skipLayer) {

    hwcLayer->setColorTransform(outputIndependentState.colorTransform);
    hwcLayer->setSurfaceDamage(surfaceDamage);

    switch (compositionType) {
        case Composition::DEVICE:
        case Composition::CURSOR:
        case Composition::DISPLAY_DECORATION:
            writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
            // ↑ DEVICE 层在这里把 GraphicBuffer 句柄交给 HWC
            break;
        case Composition::CLIENT:
            // Ignored ← CLIENT 层不在这里提交 Buffer
            //            GPU 合成后走 setClientTarget 单独提交
            break;
    }
}

writeBufferStateToHWCOutputLayer.cpp:835):

void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, ...) {
    // 从缓存获取 slot(避免重复传输同一 Buffer)
    hwcSlotAndBuffer = state.hwc->hwcBufferCache.getHwcSlotAndBuffer(
            outputIndependentState.buffer);   // App 的 GraphicBuffer
    hwcFence = outputIndependentState.acquireFence;  // App GPU 写完的 fence

    hwcLayer->setBuffer(hwcSlotAndBuffer.slot,
                        hwcSlotAndBuffer.buffer,  // GraphicBuffer handle
                        hwcFence);
    // HWC 等 acquireFence 信号后,通过 DMA 直接读像素,不经过 SF 的 GPU
}

第五步:generateClientCompositionRequests 筛选 CLIENT 层

Output.cpp:1493,只有 requiresClientComposition()=true 的层才进入 GPU 合成:

std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(...) {
    for (auto* layer : getOutputLayersOrderedByZ()) {   // 底 → 顶

        const bool clientComposition = layer->requiresClientComposition();
        // requiresClientComposition() = (hwcCompositionType == CLIENT)
        // ① 壁纸   hwcCompositionType=DEVICE → false  跳过
        // ② 桌面   hwcCompositionType=DEVICE → false  跳过
        // ③ 小窗   hwcCompositionType=CLIENT → true   ← 进入 GPU 合成
        // ④ 状态栏 hwcCompositionType=DEVICE → false  跳过
        // ⑤ 导航栏 hwcCompositionType=DEVICE → false  跳过

        if (clientComposition || clearClientComposition) {
            auto clientCompositionSettings =
                    layerFE.prepareClientComposition(targetSettings);
            clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
        }
    }
    // 返回只含 ③小窗 的列表,传入 RenderEngine
    return clientCompositionLayers;
}

第六步:LayerFE 把圆角参数打包给 RenderEngine

LayerFE::prepareClientCompositionInternalLayerFE.cpp:121):

std::optional<LayerFE::LayerSettings>
LayerFE::prepareClientCompositionInternal(...) const {
    LayerFE::LayerSettings layerSettings;

    // 圆角参数写入 LayerSettings(从 LayerSnapshot 读取)
    const auto& roundedCornerState = mSnapshot->roundedCorner;
    layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
    // ↑ 例如 vec2{20.f, 20.f}(小窗圆角半径)
    layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
    // ↑ 圆角裁剪的矩形区域(窗口边界)

    // Buffer、dataspace、alpha 等其他属性...
    prepareBufferStateClientComposition(layerSettings, targetSettings);
    return layerSettings;
}

第七步:SkiaRenderEngine 执行圆角裁剪

计算圆角 RRectSkiaRenderEngine.cpp:885):

const auto [bounds, roundRectClip] =
    getBoundsAndClip(
        layer.geometry.boundaries,           // 窗口内容边界
        layer.geometry.roundedCornersCrop,   // 圆角裁剪区域
        layer.geometry.roundedCornersRadius  // vec2{20, 20}
    );

getBoundsAndClipSkiaRenderEngine.cpp:178)三条执行路径:

static inline std::pair<SkRRect, SkRRect> getBoundsAndClip(
        const FloatRect& boundsRect,
        const FloatRect& cropRect,
        const vec2& cornerRadius) {

    if (cornerRadius.x > 0 && cornerRadius.y > 0) {

        // 路径1(最优):bounds 和 crop 一致,直接生成圆角矩形,clip 为空
        if (bounds == crop || crop.isEmpty()) {
            return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
        }

        if (crop.contains(bounds)) {
            const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
            if (insetCrop.contains(bounds)) {
                // 路径2:crop 完全包含且内缩后仍包含,不需要圆角,clip 为空
                return {SkRRect::MakeRect(bounds), clip};
            }
            // 路径3:尝试合并为单次 RRect drawcall
            if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
                SkRRect intersectionBounds;
                intersectionBounds.setRectRadii(bounds, radii);
                return {intersectionBounds, clip};
            }
        }

        // 路径4(通用):用 clip 来裁剪圆角
        clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
    }
    return {SkRRect::MakeRect(bounds), clip};
}

实际绘制SkiaRenderEngine.cpp:1237):

// 先应用圆角裁剪(如果需要)
if (!roundRectClip.isEmpty()) {
    canvas->clipRRect(roundRectClip, true);  // true = 抗锯齿裁剪
}

// 绘制内容
if (!bounds.isRect()) {
    paint.setAntiAlias(true);
    canvas->drawRRect(bounds, paint);   // 圆角边缘抗锯齿绘制
} else {
    canvas->drawRect(bounds.rect(), paint);
}

此时 clientTarget Buffer 的像素状态:

clientTarget Buffer(全屏大小):
┌────────────────────────────────────┐
│  alpha=0   alpha=0   alpha=0       │  ← 小窗以外全透明(alpha=0)
│  alpha=0  ╭──────────────╮  alpha=0│  ← 圆角处:抗锯齿半透明过渡
│  alpha=0  │              │  alpha=0│
│  alpha=0  │  小窗内容     │  alpha=0│  ← 圆角矩形内:正常像素
│  alpha=0  │              │  alpha=0│
│  alpha=0  ╰──────────────╯  alpha=0│  ← 圆角处:抗锯齿半透明过渡
│  alpha=0   alpha=0   alpha=0       │
└────────────────────────────────────┘

第八步:HWC 5 个 plane 最终合成送显

HWC validate 收到的请求:
  ① 壁纸   compositionType=DEVICE  → setBuffer(壁纸 GraphicBuffer, acquireFence)
  ② 桌面   compositionType=DEVICE  → setBuffer(桌面 GraphicBuffer, acquireFence)
  ③ 小窗   compositionType=CLIENT  → 不 setBuffer,等 setClientTarget
  ④ 状态栏 compositionType=DEVICE  → setBuffer(状态栏 GraphicBuffer, acquireFence)
  ⑤ 导航栏 compositionType=DEVICE  → setBuffer(导航栏 GraphicBuffer, acquireFence)

GPU 合成完成后(HWComposer.cpp:499):
  setClientTarget(clientTargetBuffer, acquireFence)
  ← 把含圆角小窗的 GPU 合成结果交给 HWC

HWC present → DRM AtomicCommit:
  plane 0 : ① 壁纸 Buffer     DMA 直读,零 GPU 开销
  plane 1 : ② 桌面 Buffer     DMA 直读,零 GPU 开销
  plane 2 : clientTarget      GPU 输出,小窗圆角外 alpha=0,壁纸/桌面透出
  plane 3 : ④ 状态栏 Buffer   DMA 直读,零 GPU 开销
  plane 4 : ⑤ 导航栏 Buffer   DMA 直读,零 GPU 开销
            ↓ Display Controller alpha 混合叠加
            屏幕最终画面

完整调用链

App 调用 SurfaceControl.Transaction.setCornerRadius(radius)
  └─ LayerSnapshot::roundedCorner.radius = radius
  └─ LayerSnapshot::isOpaque = false(有圆角→非不透明)

SF 合成循环(每帧)
  │
  ├─ updateCompositionState(Output.cpp:851)
  │   无 blur 层 → forceClientComposition 传播 = false
  │   小窗 state.forceClientComposition = false(圆角不触发此路径)
  │
  ├─ writeStateToHWC(OutputLayer.cpp:472)
  │   ├─ DEVICE 层(①②④⑤)
  │   │   writeBufferStateToHWC → hwcLayer->setBuffer(GraphicBuffer, fence)
  │   │
  │   └─ 小窗(③)
  │       isClientCompositionForced() = true(hasRoundedCorners=true)
  │       writeCompositionTypeToHWC → hwcLayer->setCompositionType(CLIENT)
  │
  ├─ chooseCompositionStrategy(Display.cpp:248)
  │   hwc.getDeviceCompositionChanges() → HWC validate(Binder 阻塞)
  │   HWC 确认:①②④⑤=DEVICE,③=CLIENT
  │   applyCompositionStrategy:usesClientComposition=true,usesDeviceComposition=true
  │
  ├─ composeSurfaces(Output.cpp:1337)
  │   generateClientCompositionRequests → 只取 ③小窗
  │   └─ LayerFE::prepareClientCompositionInternal
  │       roundedCornersRadius/Crop → LayerSettings
  │
  ├─ RenderEngine::drawLayersInternal(layers={③小窗})
  │   getBoundsAndClip → SkRRect bounds + roundRectClip
  │   canvas->clipRRect(roundRectClip, antialias=true)
  │   canvas->drawRRect(bounds, paint)(或 drawRect)
  │   flush surface + flushGL
  │   → clientTarget Buffer(小窗圆角内有像素,圆角外 alpha=0)
  │
  ├─ HWComposer::setClientTarget(clientTargetBuffer, acquireFence)
  │
  └─ HWComposer::presentAndGetReleaseFences
      hwcDisplay->present()
      → DRM AtomicCommit
      → Display Controller 5 plane 叠加 → 屏幕

关键结论

问题 结论
圆角是否像 blur 一样传播 CLIENT? 。圆角只在 isClientCompositionForced() 里影响自身,不传播
壁纸/桌面为何保持 DEVICE? 没有 blur 层 → forceClientComposition 不传播 → 壁纸/桌面不被强制
圆角的 GPU 裁剪在哪执行? SkiaRenderEngine::drawLayersInternal 里的 canvas->clipRRect()
DEVICE 层的 Buffer 怎么到 HWC? writeBufferStateToHWC → hwcLayer->setBuffer(GraphicBuffer, acquireFence)
CLIENT 层的结果怎么到 HWC? GPU 合成完 → HWComposer::setClientTarget(clientTargetBuffer)
GPU 节省了什么? 4 个 DEVICE 层零 GPU 开销,GPU 只处理 1 个圆角小窗