Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 028676ae authored by Alec Mouri's avatar Alec Mouri
Browse files

Allow for solid color layers to start some candidate cached sets.

This is for letterboxed holepunch: a typical layer stack would be:
1. A solid color letterbox in back
2. A solid color layer for SurfaceView's background protection
3. A game's SurfaceView which has rounded corners

One of the constraints for creating a CachedSet is that it cannot begin
with a solid color layer, under the assumption that if there is an idle
layer on top of a solid color layer, then those two layers should not be
merged into a cached set since displaying the color layer separately is
already cheap. But, for the example layer stack above, this means that
there cannot be a candidate cached set behind the game's SurfaceView,
meaning that there is no hole punch generated, so the game is always in
client composition.

So, let's remove the constraint that a CachedSet cannot start with a solid
color layer for hole punches, which resolves the issue.

Bug: 208780233
Test: Hill Climb Racing with letterboxed rounded corners
Test: libcompositionengine_test
Change-Id: Ia5bcdec363f4401b9b9738c5f6ce335497d6362b
parent 387b08fe
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -73,8 +73,9 @@ private:
    void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
                                                  bool skipLayer);
    void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
    void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
                                                  bool skipLayer);
    void writeOutputIndependentPerFrameStateToHWC(
            HWC2::Layer*, const LayerFECompositionState&,
            Hwc2::IComposerClient::Composition compositionType, bool skipLayer);
    void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
    void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
    void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer);
+29 −11
Original line number Diff line number Diff line
@@ -127,21 +127,22 @@ private:
        class Builder {
        private:
            std::vector<CachedSet>::const_iterator mStart;
            std::vector<size_t> mLengths;
            int32_t mNumSets = 0;
            const CachedSet* mHolePunchCandidate = nullptr;
            const CachedSet* mBlurringLayer = nullptr;
            bool mBuilt = false;

        public:
            // Initializes a Builder a CachedSet to start from.
            // This start iterator must be an iterator for mLayers
            void init(const std::vector<CachedSet>::const_iterator& start) {
                mStart = start;
                mLengths.push_back(start->getLayerCount());
                mNumSets = 1;
            }

            // Appends a new CachedSet to the end of the run
            // The provided length must be the size of the next sequential CachedSet in layers
            void append(size_t length) { mLengths.push_back(length); }
            void increment() { mNumSets++; }

            // Sets the hole punch candidate for the Run.
            void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
@@ -154,19 +155,36 @@ private:

            // Builds a Run instance, if a valid Run may be built.
            std::optional<Run> validateAndBuild() {
                if (mLengths.size() == 0) {
                const bool built = mBuilt;
                mBuilt = true;
                if (mNumSets <= 0 || built) {
                    return std::nullopt;
                }
                // Runs of length 1 which are hole punch candidates are allowed if the candidate is
                // going to be used.
                if (mLengths.size() == 1 &&
                    (!mHolePunchCandidate || !(mHolePunchCandidate->requiresHolePunch()))) {

                const bool requiresHolePunch =
                        mHolePunchCandidate && mHolePunchCandidate->requiresHolePunch();

                if (!requiresHolePunch) {
                    // If we don't require a hole punch, then treat solid color layers at the front
                    // to be "cheap", so remove them from the candidate cached set.
                    while (mNumSets > 1 && mStart->getLayerCount() == 1 &&
                           mStart->getFirstLayer().getBuffer() == nullptr) {
                        mStart++;
                        mNumSets--;
                    }

                    // Only allow for single cached sets if a hole punch is required. If we're here,
                    // then we don't require a hole punch, so don't build a run.
                    if (mNumSets <= 1) {
                        return std::nullopt;
                    }
                }

                return Run(mStart,
                           std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
                                       [](size_t left, size_t right) { return left + right; }),
                           std::reduce(mStart, mStart + mNumSets, 0u,
                                       [](size_t length, const CachedSet& set) {
                                           return length + set.getLayerCount();
                                       }),
                           mHolePunchCandidate, mBlurringLayer);
            }

+11 −9
Original line number Diff line number Diff line
@@ -347,6 +347,10 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t

    auto requestedCompositionType = outputIndependentState->compositionType;

    if (requestedCompositionType == hal::Composition::SOLID_COLOR && state.overrideInfo.buffer) {
        requestedCompositionType = hal::Composition::DEVICE;
    }

    // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
    // only when the geometry actually changes
    const bool isOverridden =
@@ -359,13 +363,15 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t
    }

    writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer);
    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState,
                                             requestedCompositionType, skipLayer);

    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
                              skipLayer);

    // Always set the layer color after setting the composition type.
    if (requestedCompositionType == hal::Composition::SOLID_COLOR) {
        writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
    }

    editState().hwc->stateOverridden = isOverridden;
    editState().hwc->layerSkipped = skipLayer;
@@ -477,7 +483,7 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer)

void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
        bool skipLayer) {
        hal::Composition compositionType, bool skipLayer) {
    switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
        case hal::Error::NONE:
            break;
@@ -501,7 +507,7 @@ void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
    }

    // Content-specific per-frame state
    switch (outputIndependentState.compositionType) {
    switch (compositionType) {
        case hal::Composition::SOLID_COLOR:
            // For compatibility, should be written AFTER the composition type.
            break;
@@ -521,10 +527,6 @@ void OutputLayer::writeOutputIndependentPerFrameStateToHWC(

void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
                                            const LayerFECompositionState& outputIndependentState) {
    if (outputIndependentState.compositionType != hal::Composition::SOLID_COLOR) {
        return;
    }

    hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
+5 −10
Original line number Diff line number Diff line
@@ -416,11 +416,7 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
        if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
            !currentSet->hasUnsupportedDataspace()) {
            if (isPartOfRun) {
                builder.append(currentSet->getLayerCount());
            } else {
                // Runs can't start with a non-buffer layer
                if (currentSet->getFirstLayer().getBuffer() == nullptr) {
                    ALOGV("[%s] Skipping initial non-buffer layer", __func__);
                builder.increment();
            } else {
                builder.init(currentSet);
                if (firstLayer) {
@@ -428,7 +424,6 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
                }
                isPartOfRun = true;
            }
            }
        } else if (isPartOfRun) {
            builder.setHolePunchCandidate(&(*currentSet));

+16 −0
Original line number Diff line number Diff line
@@ -1109,6 +1109,22 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}

TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) {
    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
    includeOverrideInfo();

    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
                              kOverrideBlendMode, kOverrideAlpha);
    expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
                              kOverrideSurfaceDamage);
    expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));

    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}

TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
    mOutputLayer.editState().hwc->stateOverridden = true;
Loading