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

Commit 9b24e29e authored by Ady Abraham's avatar Ady Abraham Committed by Android (Google) Code Review
Browse files

Merge "Add unit tests for android::compositionengine::impl::planner::Flattener" into sc-dev

parents cc8dc219 b7db4106
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ cc_test {
    defaults: ["libcompositionengine_defaults"],
    srcs: [
        "tests/planner/CachedSetTest.cpp",
        "tests/planner/FlattenerTest.cpp",
        "tests/CompositionEngineTest.cpp",
        "tests/DisplayColorProfileTest.cpp",
        "tests/DisplayTest.cpp",
+2 −1
Original line number Diff line number Diff line
@@ -39,7 +39,8 @@ public:

    void setDisplaySize(ui::Size size) { mDisplaySize = size; }

    NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash);
    NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
                                std::chrono::steady_clock::time_point now);

    void renderCachedSets(renderengine::RenderEngine&);

+1 −3
Original line number Diff line number Diff line
@@ -28,9 +28,7 @@ using namespace std::chrono_literals;
namespace android::compositionengine::impl::planner {

NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
                                       NonBufferHash hash) {
    const auto now = std::chrono::steady_clock::now();

                                       NonBufferHash hash, time_point now) {
    const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
    mUnflattenedDisplayCost += unflattenedDisplayCost;

+2 −1
Original line number Diff line number Diff line
@@ -89,7 +89,8 @@ void Planner::plan(
                   });

    const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
    mFlattenedHash = mFlattener.flattenLayers(mCurrentLayers, hash);
    mFlattenedHash =
            mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
    const bool layersWereFlattened = hash != mFlattenedHash;
    ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);

+448 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/impl/planner/Predictor.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>

namespace android::compositionengine {
using namespace std::chrono_literals;
using impl::planner::Flattener;
using impl::planner::LayerState;
using impl::planner::NonBufferHash;
using impl::planner::Predictor;

using testing::_;
using testing::ByMove;
using testing::ByRef;
using testing::DoAll;
using testing::Invoke;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
using testing::SetArgPointee;

namespace {

class FlattenerTest : public testing::Test {
public:
    FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
    void SetUp() override;

protected:
    void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
    void initializeFlattener(const std::vector<const LayerState*>& layers);
    void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);

    // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
    // mPredictor should be mocked and checked for expectations.
    Predictor mPredictor;
    std::unique_ptr<Flattener> mFlattener;
    renderengine::mock::RenderEngine mRenderEngine;

    const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
    std::chrono::steady_clock::time_point mTime = kStartTime;

    struct TestLayer {
        std::string name;
        mock::OutputLayer outputLayer;
        impl::OutputLayerCompositionState outputLayerCompositionState;
        // LayerFE inherits from RefBase and must be held by an sp<>
        sp<mock::LayerFE> layerFE;
        LayerFECompositionState layerFECompositionState;

        std::unique_ptr<LayerState> layerState;
    };

    static constexpr size_t kNumLayers = 5;
    std::vector<std::unique_ptr<TestLayer>> mTestLayers;
};

void FlattenerTest::SetUp() {
    for (size_t i = 0; i < kNumLayers; i++) {
        auto testLayer = std::make_unique<TestLayer>();
        auto pos = static_cast<int32_t>(i);
        std::stringstream ss;
        ss << "testLayer" << i;
        testLayer->name = ss.str();

        testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);

        testLayer->layerFECompositionState.buffer =
                new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                  GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
                                          GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
                                  "output");

        testLayer->layerFE = sp<mock::LayerFE>::make();

        EXPECT_CALL(*testLayer->layerFE, getSequence)
                .WillRepeatedly(Return(static_cast<int32_t>(i)));
        EXPECT_CALL(*testLayer->layerFE, getDebugName)
                .WillRepeatedly(Return(testLayer->name.c_str()));
        EXPECT_CALL(*testLayer->layerFE, getCompositionState)
                .WillRepeatedly(Return(&testLayer->layerFECompositionState));

        std::vector<LayerFE::LayerSettings> clientCompositionList = {
                LayerFE::LayerSettings{},
        };

        EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList)
                .WillRepeatedly(Return(clientCompositionList));
        EXPECT_CALL(testLayer->outputLayer, getLayerFE)
                .WillRepeatedly(ReturnRef(*testLayer->layerFE));
        EXPECT_CALL(testLayer->outputLayer, getState)
                .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
        EXPECT_CALL(testLayer->outputLayer, editState)
                .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));

        testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
        testLayer->layerState->incrementFramesSinceBufferUpdate();

        mTestLayers.emplace_back(std::move(testLayer));
    }
}

void FlattenerTest::initializeOverrideBuffer(const std::vector<const LayerState*>& layers) {
    for (const auto layer : layers) {
        layer->getOutputLayer()->editState().overrideInfo = {};
    }
}

void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& layers) {
    // layer stack is unknown, reset current geomentry
    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    // same geometry, update the internal layer stack
    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);
}

void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
    // layers would be flattened but the buffer would not be overridden
    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));

    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    for (const auto layer : layers) {
        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
    }

    // the new flattened layer is replaced
    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
    EXPECT_NE(nullptr, buffer);
    for (const auto layer : layers) {
        EXPECT_EQ(buffer, layer->getOutputLayer()->getState().overrideInfo.buffer);
    }
}

TEST_F(FlattenerTest, flattenLayers_NewLayerStack) {
    auto& layerState1 = mTestLayers[0]->layerState;
    auto& layerState2 = mTestLayers[1]->layerState;

    const std::vector<const LayerState*> layers = {
            layerState1.get(),
            layerState2.get(),
    };
    initializeFlattener(layers);
}

TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) {
    auto& layerState1 = mTestLayers[0]->layerState;
    auto& layerState2 = mTestLayers[1]->layerState;

    const std::vector<const LayerState*> layers = {
            layerState1.get(),
            layerState2.get(),
    };

    initializeFlattener(layers);

    // layers cannot be flattened yet, since they are still active
    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);
}

TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
    auto& layerState1 = mTestLayers[0]->layerState;
    auto& layerState2 = mTestLayers[1]->layerState;
    auto& layerState3 = mTestLayers[2]->layerState;

    const std::vector<const LayerState*> layers = {
            layerState1.get(),
            layerState2.get(),
            layerState3.get(),
    };

    initializeFlattener(layers);

    // make all layers inactive
    mTime += 200ms;
    expectAllLayersFlattened(layers);
}

TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) {
    auto& layerState1 = mTestLayers[0]->layerState;
    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState2 = mTestLayers[1]->layerState;
    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState3 = mTestLayers[2]->layerState;
    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;

    const std::vector<const LayerState*> layers = {
            layerState1.get(),
            layerState2.get(),
            layerState3.get(),
    };

    initializeFlattener(layers);

    // make all layers inactive
    mTime += 200ms;
    expectAllLayersFlattened(layers);

    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_NE(nullptr, overrideBuffer1);
    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
}

TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
    auto& layerState1 = mTestLayers[0]->layerState;
    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState2 = mTestLayers[1]->layerState;
    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState3 = mTestLayers[2]->layerState;
    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;

    std::vector<const LayerState*> layers = {
            layerState1.get(),
            layerState2.get(),
    };

    initializeFlattener(layers);
    // make all layers inactive
    mTime += 200ms;

    initializeOverrideBuffer(layers);
    expectAllLayersFlattened(layers);

    // add a new layer to the stack, this will cause all the flatenner to reset
    layers.push_back(layerState3.get());

    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_EQ(nullptr, overrideBuffer1);
    EXPECT_EQ(nullptr, overrideBuffer2);
    EXPECT_EQ(nullptr, overrideBuffer3);
}

TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
    auto& layerState1 = mTestLayers[0]->layerState;
    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState2 = mTestLayers[1]->layerState;
    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState3 = mTestLayers[2]->layerState;
    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;

    const std::vector<const LayerState*> layers = {
            layerState1.get(),
            layerState2.get(),
            layerState3.get(),
    };

    initializeFlattener(layers);

    // make all layers inactive
    mTime += 200ms;
    expectAllLayersFlattened(layers);

    // Layer 1 posted a buffer update, layers would be decomposed, and a new drawFrame would be
    // caleed for Layer2 and Layer3
    layerState1->resetFramesSinceBufferUpdate();

    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_EQ(nullptr, overrideBuffer1);
    EXPECT_EQ(nullptr, overrideBuffer2);
    EXPECT_EQ(nullptr, overrideBuffer3);

    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_EQ(nullptr, overrideBuffer1);
    EXPECT_NE(nullptr, overrideBuffer2);
    EXPECT_EQ(overrideBuffer2, overrideBuffer3);

    layerState1->incrementFramesSinceBufferUpdate();
    mTime += 200ms;

    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_EQ(nullptr, overrideBuffer1);
    EXPECT_NE(nullptr, overrideBuffer2);
    EXPECT_EQ(overrideBuffer2, overrideBuffer3);

    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_NE(nullptr, overrideBuffer1);
    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
}

TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
    auto& layerState1 = mTestLayers[0]->layerState;
    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState2 = mTestLayers[1]->layerState;
    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState3 = mTestLayers[2]->layerState;
    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState4 = mTestLayers[3]->layerState;
    const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;

    auto& layerState5 = mTestLayers[4]->layerState;
    const auto& overrideBuffer5 = layerState5->getOutputLayer()->getState().overrideInfo.buffer;

    const std::vector<const LayerState*> layers = {
            layerState1.get(), layerState2.get(), layerState3.get(),
            layerState4.get(), layerState5.get(),
    };

    initializeFlattener(layers);

    // make all layers inactive
    mTime += 200ms;
    expectAllLayersFlattened(layers);

    // Layer 3 posted a buffer update, layers would be decomposed, and a new drawFrame would be
    // called for Layer1 and Layer2
    layerState3->resetFramesSinceBufferUpdate();

    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
    initializeOverrideBuffer(layers);
    EXPECT_EQ(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_EQ(nullptr, overrideBuffer1);
    EXPECT_EQ(nullptr, overrideBuffer2);
    EXPECT_EQ(nullptr, overrideBuffer3);
    EXPECT_EQ(nullptr, overrideBuffer4);
    EXPECT_EQ(nullptr, overrideBuffer5);

    // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_NE(nullptr, overrideBuffer1);
    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
    EXPECT_EQ(nullptr, overrideBuffer3);
    EXPECT_EQ(nullptr, overrideBuffer4);
    EXPECT_EQ(nullptr, overrideBuffer5);

    // Layers 4 and 5 will be flattened
    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_NE(nullptr, overrideBuffer1);
    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
    EXPECT_EQ(nullptr, overrideBuffer3);
    EXPECT_NE(nullptr, overrideBuffer4);
    EXPECT_EQ(overrideBuffer4, overrideBuffer5);

    layerState3->incrementFramesSinceBufferUpdate();
    mTime += 200ms;

    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_NE(nullptr, overrideBuffer1);
    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
    EXPECT_EQ(nullptr, overrideBuffer3);
    EXPECT_NE(nullptr, overrideBuffer4);
    EXPECT_EQ(overrideBuffer4, overrideBuffer5);

    initializeOverrideBuffer(layers);
    EXPECT_NE(getNonBufferHash(layers),
              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
    mFlattener->renderCachedSets(mRenderEngine);

    EXPECT_NE(nullptr, overrideBuffer1);
    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
    EXPECT_EQ(overrideBuffer3, overrideBuffer4);
    EXPECT_EQ(overrideBuffer4, overrideBuffer5);
}

} // namespace
} // namespace android::compositionengine