diff options
| -rw-r--r-- | services/surfaceflinger/CompositionEngine/Android.bp | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp | 318 |
2 files changed, 319 insertions, 0 deletions
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index f9e5b9a801..88998b06e6 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -103,6 +103,7 @@ cc_test { test_suites: ["device-tests"], defaults: ["libcompositionengine_defaults"], srcs: [ + "tests/planner/CachedSetTest.cpp", "tests/CompositionEngineTest.cpp", "tests/DisplayColorProfileTest.cpp", "tests/DisplayTest.cpp", diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp new file mode 100644 index 0000000000..6d1ce4cc7e --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -0,0 +1,318 @@ +/* + * 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/LayerState.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 testing::_; +using testing::DoAll; +using testing::Invoke; +using testing::Return; +using testing::ReturnRef; +using testing::SetArgPointee; + +using impl::planner::CachedSet; +using impl::planner::LayerState; +using impl::planner::LayerStateField; + +namespace { + +class CachedSetTest : public testing::Test { +public: + CachedSetTest() = default; + void SetUp() override; + void TearDown() override; + +protected: + const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now(); + + struct TestLayer { + 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; + std::unique_ptr<CachedSet::Layer> cachedSetLayer; + }; + + static constexpr size_t kNumLayers = 5; + std::vector<std::unique_ptr<TestLayer>> mTestLayers; + + android::renderengine::mock::RenderEngine mRenderEngine; +}; + +void CachedSetTest::SetUp() { + for (size_t i = 0; i < kNumLayers; i++) { + auto testLayer = std::make_unique<TestLayer>(); + auto pos = static_cast<int32_t>(i); + testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1); + + 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")); + EXPECT_CALL(*testLayer->layerFE, getCompositionState) + .WillRepeatedly(Return(&testLayer->layerFECompositionState)); + EXPECT_CALL(testLayer->outputLayer, getLayerFE) + .WillRepeatedly(ReturnRef(*testLayer->layerFE)); + EXPECT_CALL(testLayer->outputLayer, getState) + .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState)); + + testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer); + testLayer->layerState->incrementFramesSinceBufferUpdate(); + testLayer->cachedSetLayer = + std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime); + + mTestLayers.emplace_back(std::move(testLayer)); + } +} + +void CachedSetTest::TearDown() { + mTestLayers.clear(); +} + +void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) { + EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint()); + EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate()); + EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds()); + EXPECT_EQ(1u, cachedSet.getLayerCount()); + EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState()); + EXPECT_EQ(0u, cachedSet.getAge()); + EXPECT_EQ(layer.getHash(), cachedSet.getNonBufferHash()); +} + +void expectEqual(const CachedSet& cachedSet, const LayerState& layerState, + std::chrono::steady_clock::time_point lastUpdate) { + CachedSet::Layer layer(&layerState, lastUpdate); + expectEqual(cachedSet, layer); +} + +void expectNoBuffer(const CachedSet& cachedSet) { + EXPECT_EQ(nullptr, cachedSet.getBuffer()); + EXPECT_EQ(nullptr, cachedSet.getDrawFence()); + EXPECT_FALSE(cachedSet.hasReadyBuffer()); +} + +void expectReadyBuffer(const CachedSet& cachedSet) { + EXPECT_NE(nullptr, cachedSet.getBuffer()); + EXPECT_NE(nullptr, cachedSet.getDrawFence()); + EXPECT_TRUE(cachedSet.hasReadyBuffer()); +} + +TEST_F(CachedSetTest, createFromLayer) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet cachedSet(layer); + expectEqual(cachedSet, layer); + expectNoBuffer(cachedSet); +} + +TEST_F(CachedSetTest, createFromLayerState) { + LayerState& layerState = *mTestLayers[0]->layerState.get(); + CachedSet cachedSet(&layerState, kStartTime); + expectEqual(cachedSet, layerState, kStartTime); + expectNoBuffer(cachedSet); +} + +TEST_F(CachedSetTest, addLayer) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint()); + EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); + EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds()); + EXPECT_EQ(2u, cachedSet.getLayerCount()); + EXPECT_EQ(0u, cachedSet.getAge()); + expectNoBuffer(cachedSet); + // TODO(b/181192080): check that getNonBufferHash returns the correct hash value + // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()), + // cachedSet.getNonBufferHash()); +} + +TEST_F(CachedSetTest, decompose) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); + + std::vector<CachedSet> decomposed = cachedSet.decompose(); + EXPECT_EQ(3u, decomposed.size()); + expectEqual(decomposed[0], *layer1.getState(), kStartTime); + expectNoBuffer(decomposed[0]); + + expectEqual(decomposed[1], *layer2.getState(), kStartTime + 10ms); + expectNoBuffer(decomposed[1]); + + expectEqual(decomposed[2], *layer3.getState(), kStartTime + 20ms); + expectNoBuffer(decomposed[2]); +} + +TEST_F(CachedSetTest, setLastUpdate) { + LayerState& layerState = *mTestLayers[0]->layerState.get(); + CachedSet cachedSet(&layerState, kStartTime); + cachedSet.setLastUpdate(kStartTime + 10ms); + expectEqual(cachedSet, layerState, kStartTime + 10ms); +} + +TEST_F(CachedSetTest, incrementAge) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet cachedSet(layer); + EXPECT_EQ(0u, cachedSet.getAge()); + cachedSet.incrementAge(); + EXPECT_EQ(1u, cachedSet.getAge()); + cachedSet.incrementAge(); + EXPECT_EQ(2u, cachedSet.getAge()); +} + +TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); + + std::vector<const LayerState*> incomingLayers = { + layer1.getState(), + layer2.getState(), + layer3.getState(), + }; + + EXPECT_FALSE(cachedSet.hasBufferUpdate(incomingLayers.begin())); +} + +TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); + + mTestLayers[1]->layerState->resetFramesSinceBufferUpdate(); + + std::vector<const LayerState*> incomingLayers = { + layer1.getState(), + layer2.getState(), + layer3.getState(), + }; + + EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin())); +} + +TEST_F(CachedSetTest, append) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet1(layer1); + CachedSet cachedSet2(layer2); + cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms); + cachedSet1.append(cachedSet2); + + EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint()); + EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate()); + EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds()); + EXPECT_EQ(3u, cachedSet1.getLayerCount()); + EXPECT_EQ(0u, cachedSet1.getAge()); + expectNoBuffer(cachedSet1); + // TODO(b/181192080): check that getNonBufferHash returns the correct hash value + // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()), + // cachedSet1.getNonBufferHash()); +} + +TEST_F(CachedSetTest, updateAge_NoUpdate) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + + CachedSet cachedSet(layer); + cachedSet.incrementAge(); + EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); + EXPECT_EQ(1u, cachedSet.getAge()); + + cachedSet.updateAge(kStartTime + 10ms); + EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); + EXPECT_EQ(1u, cachedSet.getAge()); +} + +TEST_F(CachedSetTest, updateAge_BufferUpdate) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + mTestLayers[0]->layerState->resetFramesSinceBufferUpdate(); + + CachedSet cachedSet(layer); + cachedSet.incrementAge(); + EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); + EXPECT_EQ(1u, cachedSet.getAge()); + + cachedSet.updateAge(kStartTime + 10ms); + EXPECT_EQ(kStartTime + 10ms, cachedSet.getLastUpdate()); + EXPECT_EQ(0u, cachedSet.getAge()); +} + +TEST_F(CachedSetTest, render) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE; + + CachedSet cachedSet(layer1); + cachedSet.append(CachedSet(layer2)); + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; + clientCompList1.push_back({}); + clientCompList1[0].alpha = 0.5f; + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; + clientCompList2.push_back({}); + clientCompList2[0].alpha = 0.75f; + + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layers, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*) -> size_t { + EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay); + EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip); + EXPECT_EQ(0.5f, layers[0]->alpha); + EXPECT_EQ(0.75f, layers[1]->alpha); + + return NO_ERROR; + }; + + EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); + EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + cachedSet.render(mRenderEngine); + expectReadyBuffer(cachedSet); +} + +} // namespace +} // namespace android::compositionengine |