diff options
| author | 2021-03-15 22:13:46 +0000 | |
|---|---|---|
| committer | 2021-03-15 22:13:46 +0000 | |
| commit | 99ebe3f9f63602d22d6f1028b2996bac6665dd07 (patch) | |
| tree | e0d21646f5505d4ad02ba3fd1c900049f7b088b2 | |
| parent | 858944dfda6958620d9979adc38a23a98ffa1061 (diff) | |
| parent | f6a684b8be0bc8fe6b0a4d87a454077abc643e26 (diff) | |
Merge "Add tests for LayerStack::ApproximateMatch" into sc-dev
4 files changed, 355 insertions, 2 deletions
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index d297d74ce5..08147edcfc 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -106,6 +106,7 @@ cc_test { "tests/planner/CachedSetTest.cpp", "tests/planner/FlattenerTest.cpp", "tests/planner/LayerStateTest.cpp", + "tests/planner/PredictorTest.cpp", "tests/CompositionEngineTest.cpp", "tests/DisplayColorProfileTest.cpp", "tests/DisplayTest.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h index 422af771f3..55d2a60906 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h @@ -24,16 +24,28 @@ class LayerStack { public: LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {} + // Describes an approximate match between two layer stacks struct ApproximateMatch { bool operator==(const ApproximateMatch& other) const { return differingIndex == other.differingIndex && differingFields == other.differingFields; } + // The index of the single differing layer between the two stacks. + // This implies that only one layer is allowed to differ in an approximate match. size_t differingIndex; + // Set of fields that differ for the differing layer in the approximate match. Flags<LayerStateField> differingFields; }; + // Returns an approximate match when comparing this layer stack with the provided list of + // layers, for the purposes of scoring how closely the two layer stacks will match composition + // strategies. + // + // If the two layer stacks are identical, then an approximate match is still returned, but the + // differing fields will be empty to represent an exact match. + // + // If the two layer stacks differ by too much, then an empty optional is returned. std::optional<ApproximateMatch> getApproximateMatch( const std::vector<const LayerState*>& other) const; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp index ba5e64dec6..07920b8313 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp @@ -50,8 +50,8 @@ std::optional<LayerStack::ApproximateMatch> LayerStack::getApproximateMatch( return std::nullopt; } - // If layers are not identical, but we already have a prior approximate match, - // the LayerStacks differ by too much, so return nothing + // If layers are not identical, but we already detected a prior approximate match for a + // previous layer, the LayerStacks differ by too much, so return nothing if (approximateMatch) { return std::nullopt; } @@ -72,6 +72,10 @@ std::optional<LayerStack::ApproximateMatch> LayerStack::getApproximateMatch( } } + if (approximateMatch) { + return approximateMatch; + } + // If we make it through the layer-by-layer comparison without an approximate match, // it means that all layers were either identical or had client-composited layers in common, // which don't affect the composition strategy, so return a successful result with diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp new file mode 100644 index 0000000000..abd64d4d6f --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp @@ -0,0 +1,336 @@ +/* + * 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 "DisplayHardware/Hal.h" +#undef LOG_TAG +#define LOG_TAG "LayerStateTest" + +#include <compositionengine/impl/planner/Predictor.h> +#include <compositionengine/mock/LayerFE.h> +#include <compositionengine/mock/OutputLayer.h> +#include <gtest/gtest.h> +#include <log/log.h> + +namespace android::compositionengine::impl::planner { +namespace { + +const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f); +const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f); +const Rect sRectOne = Rect(1, 2, 3, 4); +const Rect sRectTwo = Rect(4, 3, 2, 1); +const constexpr int32_t sZOne = 100; +const constexpr int32_t sZTwo = 101; +const constexpr float sAlphaOne = 0.25f; +const constexpr float sAlphaTwo = 0.5f; +const Region sRegionOne = Region(sRectOne); +const Region sRegionTwo = Region(sRectTwo); +const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f)); + +using testing::Return; +using testing::ReturnRef; + +const std::string sDebugName = std::string("Test LayerFE"); +const constexpr int32_t sSequenceId = 12345; + +struct LayerStackTest : public testing::Test { + LayerStackTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~LayerStackTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE, + const OutputLayerCompositionState& outputLayerState, + const LayerFECompositionState& layerFEState) { + EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); + EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState)); + EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sSequenceId)); + EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(sDebugName.c_str())); + EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState)); + } +}; + +TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne; + LayerFECompositionState layerFECompositionStateOne; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo; + LayerFECompositionState layerFECompositionStateTwo; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + mock::OutputLayer outputLayerThree; + mock::LayerFE layerFEThree; + OutputLayerCompositionState outputLayerCompositionStateThree; + LayerFECompositionState layerFECompositionStateThree; + setupMocksForLayer(outputLayerThree, layerFEThree, outputLayerCompositionStateThree, + layerFECompositionStateThree); + LayerState layerStateThree(&outputLayerThree); + + LayerStack stack({&layerStateOne}); + + EXPECT_FALSE(stack.getApproximateMatch({})); + EXPECT_FALSE(stack.getApproximateMatch({&layerStateOne, &layerStateThree})); +} + +TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne; + LayerFECompositionState layerFECompositionStateOne; + layerFECompositionStateOne.compositionType = hal::Composition::DEVICE; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + LayerStack stack({&layerStateOne}); + + EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo})); +} + +TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne{ + .sourceCrop = sFloatRectOne, + }; + LayerFECompositionState layerFECompositionStateOne; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo{ + .sourceCrop = sFloatRectTwo, + }; + LayerFECompositionState layerFECompositionStateTwo; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + LayerStack stack({&layerStateOne}); + + const auto match = stack.getApproximateMatch({&layerStateTwo}); + EXPECT_TRUE(match); + LayerStack::ApproximateMatch expectedMatch; + expectedMatch.differingIndex = 0; + expectedMatch.differingFields = LayerStateField::SourceCrop; + EXPECT_EQ(expectedMatch, *match); +} + +TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne{ + .sourceCrop = sFloatRectOne, + }; + LayerFECompositionState layerFECompositionStateOne; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo{ + .sourceCrop = sFloatRectTwo, + }; + LayerFECompositionState layerFECompositionStateTwo; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + LayerStack stack({&layerStateOne, &layerStateOne}); + + const auto match = stack.getApproximateMatch({&layerStateOne, &layerStateTwo}); + EXPECT_TRUE(match); + LayerStack::ApproximateMatch expectedMatch; + expectedMatch.differingIndex = 1; + expectedMatch.differingFields = LayerStateField::SourceCrop; + EXPECT_EQ(expectedMatch, *match); +} + +TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne{ + .visibleRegion = sRegionOne, + .displayFrame = sRectOne, + .sourceCrop = sFloatRectOne, + .dataspace = ui::Dataspace::SRGB, + .z = sZOne, + }; + LayerFECompositionState layerFECompositionStateOne; + layerFECompositionStateOne.alpha = sAlphaOne; + layerFECompositionStateOne.colorTransformIsIdentity = true; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo{ + .visibleRegion = sRegionTwo, + .displayFrame = sRectTwo, + .sourceCrop = sFloatRectTwo, + .dataspace = ui::Dataspace::DISPLAY_P3, + .z = sZTwo, + }; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.alpha = sAlphaTwo; + layerFECompositionStateTwo.colorTransformIsIdentity = false; + layerFECompositionStateTwo.colorTransform = sMat4One; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + LayerStack stack({&layerStateOne}); + + EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo})); +} + +TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) { + sp<GraphicBuffer> buffer = new GraphicBuffer(); + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne; + LayerFECompositionState layerFECompositionStateOne; + layerFECompositionStateOne.buffer = buffer; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.buffer = buffer; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + LayerStack stack({&layerStateOne}); + + const auto match = stack.getApproximateMatch({&layerStateTwo}); + EXPECT_TRUE(match); + LayerStack::ApproximateMatch expectedMatch; + expectedMatch.differingIndex = 0; + expectedMatch.differingFields = LayerStateField::None; + EXPECT_EQ(expectedMatch, *match); +} + +TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne{ + .visibleRegion = sRegionOne, + .forceClientComposition = true, + .displayFrame = sRectOne, + .sourceCrop = sFloatRectOne, + .dataspace = ui::Dataspace::SRGB, + .z = sZOne, + }; + LayerFECompositionState layerFECompositionStateOne; + layerFECompositionStateOne.buffer = new GraphicBuffer(); + layerFECompositionStateOne.alpha = sAlphaOne; + layerFECompositionStateOne.colorTransformIsIdentity = true; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo{ + .visibleRegion = sRegionTwo, + .forceClientComposition = true, + .displayFrame = sRectTwo, + .sourceCrop = sFloatRectTwo, + .dataspace = ui::Dataspace::DISPLAY_P3, + .z = sZTwo, + }; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.buffer = new GraphicBuffer(); + layerFECompositionStateTwo.alpha = sAlphaTwo; + layerFECompositionStateTwo.colorTransformIsIdentity = false; + layerFECompositionStateTwo.colorTransform = sMat4One; + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + LayerStack stack({&layerStateOne}); + + const auto match = stack.getApproximateMatch({&layerStateTwo}); + EXPECT_TRUE(match); + LayerStack::ApproximateMatch expectedMatch; + expectedMatch.differingIndex = 0; + expectedMatch.differingFields = LayerStateField::None; + EXPECT_EQ(expectedMatch, *match); +} + +TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne{ + .sourceCrop = sFloatRectOne, + }; + LayerFECompositionState layerFECompositionStateOne; + layerFECompositionStateOne.buffer = new GraphicBuffer(); + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + mock::OutputLayer outputLayerTwo; + mock::LayerFE layerFETwo; + OutputLayerCompositionState outputLayerCompositionStateTwo{ + .sourceCrop = sFloatRectTwo, + }; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.buffer = new GraphicBuffer(); + setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + layerFECompositionStateTwo); + LayerState layerStateTwo(&outputLayerTwo); + + EXPECT_TRUE(LayerStack({&layerStateOne}).getApproximateMatch({&layerStateTwo})); + + LayerStack stack({&layerStateOne, &layerStateOne}); + EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo, &layerStateTwo})); +} + +} // namespace +} // namespace android::compositionengine::impl::planner
\ No newline at end of file |