Android SurfaceFlinger——SF图层预合成(四十)

        上一节我们简单介绍了 SurfaceFlinger 中的图层合成流程,以及图层合成前的预处理操作,这里我们来看一下下一步操作——预合成

  • output->prepare:进行图层预合成。

一、图层预合成

1、Output.cpp

源码位置:/frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

prepare

void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs, LayerFESet& geomSnapshots) {
    ……
    rebuildLayerStacks(refreshArgs, geomSnapshots);
}

        Output 实际上就是 display,这里通过调用每个 Display 的 rebuildLayerStacks ,建立display 的 LayerStacks。

rebuildLayerStacks

void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
                                LayerFESet& layerFESet) {
    ATRACE_CALL();
    ALOGV(__FUNCTION__);

    // 获取当前输出设备的状态
    auto& outputState = editState();

    // 如果未启用此输出或不需要执行此更新,则不执行任何操作
    if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
        return;
    }

    // 处理这些层以确定可见性和覆盖率
    compositionengine::Output::CoverageState coverage{layerFESet};
    collectVisibleLayers(refreshArgs, coverage);

    // 计算输出设备上的未定义区域(即没有被任何不透明图层覆盖的区域)。
    const ui::Transform& tr = outputState.transform;
    Region undefinedRegion{outputState.displaySpace.getBoundsAsRect()};
    undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));

    // 记录未定义区域和脏区信息
    outputState.undefinedRegion = undefinedRegion;
    outputState.dirtyRegion.orSelf(coverage.dirtyRegion);
}

        该方法确保了在每次合成前,输出设备的图层堆栈都被正确地重建和更新,只对真正需要更新的部分进行合成,从而提高了合成的效率和性能。

collectVisibleLayers

void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
                                  compositionengine::Output::CoverageState& coverage) {
    // 从顶层(最前面)的图层开始向后遍历所有图层。
    for (auto layer : reversed(refreshArgs.layers)) {
        // 检查图层是否在当前输出设备上可见,并更新coverage对象中的覆盖信息。
        ensureOutputLayerIfVisible(layer, coverage);
    
        ……
    }

    // 更新哪些图层已经从输出设备上移除或不再需要的信息
    setReleasedLayers(refreshArgs);
    // 对那些即将被添加到输出设备上的图层进行最终的处理,如更新内部状态、准备合成资源等。
    finalizePendingOutputLayers();
}

        该方法能够高效地确定哪些图层在当前输出设备上是可见的,同时计算这些图层的覆盖信息。这里从顶层(最前面)的图层开始向后遍历的原因是,顶层的图层可能会遮挡底层的图层,因此需要先处理顶层图层,才能准确地确定哪些底层图层是可见的。

ensureOutputLayerIfVisible

void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, compositionengine::Output::CoverageState& coverage) {
    // 初始化图层快照。确保每个候选图层的快照仅在每帧中创建一次,以避免重复工作
    if (!coverage.latchedLayers.count(layerFE)) {
        coverage.latchedLayers.insert(layerFE);
        layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
    }

    // 检查图层是否属于当前输出
    if (!includesLayer(layerFE)) {
        return;
    }

    // 获取图层前端状态
    const auto* layerFEState = layerFE->getCompositionState();
    if (CC_UNLIKELY(!layerFEState)) {
        return;
    }

    // 如果图层不可见,将可见区域设置为空,跳过后续处理
    if (CC_UNLIKELY(!layerFEState->isVisible)) {
        return;
    }

    // 完全不透明的区域
    Region opaqueRegion;
    // 屏幕上可见但不完全透明的区域
    Region visibleRegion;
    // 所有可见区域(包括半透明区域)所覆盖的区域
    Region coveredRegion;
    // 完全透明的区域
    Region transparentRegion;
    // 由层的阴影投射的区域
    Region shadowRegion;

    const ui::Transform& tr = layerFEState->geomLayerTransform;

    // 使用图层的几何变换和边界计算可见区域
    const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
    visibleRegion.set(visibleRect);

    if (layerFEState->shadowRadius > 0.0f) {
        // 如果图层投射阴影,调整可见区域以包含阴影区域
        const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
        Rect visibleRectWithShadows(visibleRect);
        visibleRectWithShadows.inset(inset, inset, inset, inset);
        visibleRegion.set(visibleRectWithShadows);
        shadowRegion = visibleRegion.subtract(visibleRect);
    }

    if (visibleRegion.isEmpty()) {
        return;
    }

    // 从可见区域中移除透明区域
    if (!layerFEState->isOpaque) {
        if (tr.preserveRects()) {
            // 变换透明区域
            transparentRegion = tr.transform(layerFEState->transparentRegionHint);
        } else {
            // 变换太复杂,不能做透明区域的优化
            transparentRegion.clear();
        }
    }

    // 计算不透明区域
    // 如果图层是完全不透明的并且变换简单(仅旋转和平移),不透明区域等于可见区域;否则,整个图层被视为半透明。
    const auto layerOrientation = tr.getOrientation();
    if (layerFEState->isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
        opaqueRegion.set(visibleRect);
    }

    // 将覆盖区域剪切到可见区域
    coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion);
    // 更新下一个(较低)层的accumAboveCoveredLayers
    coverage.aboveCoveredLayers.orSelf(visibleRegion);
    // 减去我们上面图层覆盖的不透明区域
    visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);

    if (visibleRegion.isEmpty()) {
        return;
    }

    ……
    // 根据新旧可见区域和覆盖区域计算图层的脏区,即需要更新的区域。
    Region dirty;
    if (layerFEState->contentDirty) {
        // 让整个区域失效
        dirty = visibleRegion;
        // 像旧的可见区域一样
        dirty.orSelf(oldVisibleRegion);
    } else {
        /* 计算暴露区域,暴露区域由两部分组成:
         *   1) 现在是可见的,以前是覆盖的
         *   2) 现在暴露的内容减去以前暴露的内容
         */
        const Region newExposed = visibleRegion - coveredRegion;
        const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
        dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed);
    }
    dirty.subtractSelf(coverage.aboveOpaqueLayers);

    // 计算最终可见非透明区域(积累脏区至屏幕脏区)
    coverage.dirtyRegion.orSelf(dirty);

    // 确定图层在输出设备上的实际可见区域
    coverage.aboveOpaqueLayers.orSelf(opaqueRegion);

    // 计算可见的非透明区域
    Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);

    // 执行最后的检查,看看这个层在这个输出上是否可见
    const auto& outputState = getState();
    Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
    drawRegion.andSelf(outputState.displaySpace.getBoundsAsRect());
    if (drawRegion.isEmpty()) {
        return;
    }

    // 处理阴影区域
    Region visibleNonShadowRegion = visibleRegion.subtract(shadowRegion);

    // 确保当前图层的输出层存在,如果不存在,则创建一个新的输出层
    auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);

    // 将层覆盖信息存储到层状态中
    auto& outputLayerState = result->editState();
    outputLayerState.visibleRegion = visibleRegion;
    outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
    outputLayerState.coveredRegion = coveredRegion;
    outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
            visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent()));
    outputLayerState.shadowRegion = shadowRegion;
    outputLayerState.outputSpaceBlockingRegionHint = layerFEState->compositionType == Composition::DISPLAY_DECORATION 
            ? outputState.transform.transform(transparentRegion.intersect(outputState.layerStackSpace.getContent()))
            : Region();
}

        该函数主要负责确定一个图层是否在当前输出设备上可见,并计算该图层的覆盖信息。

2、图形介绍

        为了便于理解,我们假设有一个简单的场景,其中包含一个图层 L,该图层可能有透明、不透明、阴影以及被其他图层覆盖的部分。下面将逐步介绍这些区域是如何计算的。

可见区域(Visible Region)

  • 定义:图层 L 在屏幕上的投影区域,即图层在当前输出设备上可见的部分。
  • 计算:使用图层的几何变换和边界来确定可见区域。如果图层投射阴影,可见区域会根据阴影的大小进行适当的调整。
  • 图形表示:一个矩形区域,代表图层 L 在屏幕上的可见投影。

不透明区域(Opaque Region)

  • 定义:图层 L 中完全不透明的部分,这部分不会让背景或其他下层图层透过。
  • 计算:如果图层是完全不透明的并且变换简单(例如,仅旋转和平移),则不透明区域等于可见区域;否则,整个图层被视为半透明。
  • 图形表示:在可见区域内的一个子区域,通常表示为实心颜色,代表不透明的部分。

被覆盖区域(Covered Region)

  • 定义:图层L上被其他图层遮挡的部分。
  • 计算:通过与上层图层的可见区域进行比较,确定哪些部分被遮挡。
  • 图形表示:在可见区域内,被其他图层遮挡的部分,通常表示为交叉阴影或者不同的颜色。

透明区域(Transparent Region)

  • 定义:图层L中完全透明的部分,可以让背景或其他下层图层透过。
  • 计算:如果图层不是完全不透明的,尝试计算透明区域。如果变换过于复杂,透明区域可能被清空。
  • 图形表示:在可见区域内,完全透明的部分,通常表示为空白或者透明色。

脏区(Dirty Region)

  • 定义:需要重新绘制的区域,包括图层的新可见区域以及之前可见但现在被遮挡的区域。
  • 计算:基于新旧可见区域和覆盖区域的变化来确定。
  • 图形表示:需要重新绘制的区域,通常表示为一个或多个矩形,覆盖了所有需要更新的区域。

可见非透明区域(Visible Non-Transparent Region)

  • 定义:图层L中可见但不是完全透明的部分,排除了透明区域。
  • 计算:从可见区域中减去透明区域。
  • 图形表示:在可见区域内,除去透明区域的部分,通常表示为实心颜色或半透明色。

阴影区域(Shadow Region)

  • 定义:图层L投射的阴影区域。
  • 计算:如果图层有阴影效果,阴影区域会根据阴影的大小和方向进行计算。
  • 图形表示:在可见区域外,由图层投射的阴影,通常表示为较暗的色调。

        通过这些图形表示,我们可以清楚地看到各个区域之间的关系以及它们是如何相互作用的。这些区域的计算是合成引擎决定哪些部分需要更新的关键,从而避免了不必要的渲染,提高了效率。 

  • 14
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c小旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值