diff options
| author | 2021-03-16 02:40:36 +0000 | |
|---|---|---|
| committer | 2021-03-16 02:40:36 +0000 | |
| commit | ead2fee4d9f20d9175c57170181bbd0b9571f60e (patch) | |
| tree | 394b97acd037fcbf8dfda46dec6e668decce498c | |
| parent | 732ff4429c35c14d5cf7b32261f6354b877d8f9f (diff) | |
| parent | f2b3cde725ebd552f4ee36cb025696deb31c7bcf (diff) | |
Merge "Add additional predictor unit tests" into sc-dev
| -rw-r--r-- | services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h | 35 | ||||
| -rw-r--r-- | services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp | 214 |
2 files changed, 236 insertions, 13 deletions
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h index 55d2a60906..fe486d3327 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h @@ -120,6 +120,10 @@ public: } friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); } + friend std::ostream& operator<<(std::ostream& os, const Plan& plan) { + return os << to_string(plan); + } + private: std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes; }; @@ -158,6 +162,10 @@ public: } } + friend std::ostream& operator<<(std::ostream& os, const Type& type) { + return os << to_string(type); + } + Prediction(const std::vector<const LayerState*>& layers, Plan plan) : mExampleLayerStack(layers), mPlan(std::move(plan)) {} @@ -217,11 +225,25 @@ public: NonBufferHash hash; Plan plan; Prediction::Type type; + + friend bool operator==(const PredictedPlan& lhs, const PredictedPlan& rhs) { + return lhs.hash == rhs.hash && lhs.plan == rhs.plan && lhs.type == rhs.type; + } }; - std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&, - NonBufferHash) const; + // Retrieves the predicted plan based on a layer stack alongside its hash. + // + // If the exact layer stack has previously been seen by the predictor, then report the plan used + // for that layer stack. + // + // Otherwise, try to match to the best approximate stack to retireve the most likely plan. + std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>& layers, + NonBufferHash hash) const; + // Records a comparison between the predicted plan and the resulting plan, alongside the layer + // stack we used. + // + // This method is intended to help with scoring how effective the prediction engine is. void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash, const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result); @@ -287,4 +309,13 @@ private: mutable size_t mMissCount = 0; }; +// Defining PrintTo helps with Google Tests. +inline void PrintTo(Predictor::PredictedPlan plan, ::std::ostream* os) { + *os << "PredictedPlan {"; + *os << "\n .hash = " << plan.hash; + *os << "\n .plan = " << plan.plan; + *os << "\n .type = " << plan.type; + *os << "\n}"; +} + } // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp index abd64d4d6f..43e119fc11 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp @@ -16,7 +16,7 @@ #include "DisplayHardware/Hal.h" #undef LOG_TAG -#define LOG_TAG "LayerStateTest" +#define LOG_TAG "PredictorTest" #include <compositionengine/impl/planner/Predictor.h> #include <compositionengine/mock/LayerFE.h> @@ -45,6 +45,16 @@ using testing::ReturnRef; const std::string sDebugName = std::string("Test LayerFE"); const constexpr int32_t sSequenceId = 12345; +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)); +} + struct LayerStackTest : public testing::Test { LayerStackTest() { const ::testing::TestInfo* const test_info = @@ -57,16 +67,6 @@ struct LayerStackTest : public testing::Test { ::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) { @@ -332,5 +332,197 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) { EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo, &layerStateTwo})); } +struct PredictionTest : public testing::Test { + PredictionTest() { + 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()); + } + + ~PredictionTest() { + 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()); + } +}; + +TEST_F(PredictionTest, constructPrediction) { + Plan plan; + plan.addLayerType(hal::Composition::DEVICE); + + Prediction prediction({}, plan); + + EXPECT_EQ(plan, prediction.getPlan()); + + // check that dump doesn't crash + std::string result; + prediction.dump(result); +} + +TEST_F(PredictionTest, recordHits) { + Prediction prediction({}, {}); + + const constexpr uint32_t kExactMatches = 2; + for (uint32_t i = 0; i < kExactMatches; i++) { + prediction.recordHit(Prediction::Type::Exact); + } + + const constexpr uint32_t kApproximateMatches = 3; + for (uint32_t i = 0; i < kApproximateMatches; i++) { + prediction.recordHit(Prediction::Type::Approximate); + } + + EXPECT_EQ(kExactMatches, prediction.getHitCount(Prediction::Type::Exact)); + EXPECT_EQ(kApproximateMatches, prediction.getHitCount(Prediction::Type::Approximate)); + EXPECT_EQ(kExactMatches + kApproximateMatches, prediction.getHitCount(Prediction::Type::Total)); +} + +TEST_F(PredictionTest, recordMisses) { + Prediction prediction({}, {}); + + const constexpr uint32_t kExactMatches = 2; + for (uint32_t i = 0; i < kExactMatches; i++) { + prediction.recordMiss(Prediction::Type::Exact); + } + + const constexpr uint32_t kApproximateMatches = 3; + for (uint32_t i = 0; i < kApproximateMatches; i++) { + prediction.recordMiss(Prediction::Type::Approximate); + } + + EXPECT_EQ(kExactMatches, prediction.getMissCount(Prediction::Type::Exact)); + EXPECT_EQ(kApproximateMatches, prediction.getMissCount(Prediction::Type::Approximate)); + EXPECT_EQ(kExactMatches + kApproximateMatches, + prediction.getMissCount(Prediction::Type::Total)); +} + +struct PredictorTest : public testing::Test { + PredictorTest() { + 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()); + } + + ~PredictorTest() { + 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()); + } +}; + +TEST_F(PredictorTest, getPredictedPlan_emptyLayersWithoutExactMatch_returnsNullopt) { + Predictor predictor; + EXPECT_FALSE(predictor.getPredictedPlan({}, 0)); +} + +TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) { + mock::OutputLayer outputLayerOne; + mock::LayerFE layerFEOne; + OutputLayerCompositionState outputLayerCompositionStateOne; + LayerFECompositionState layerFECompositionStateOne; + layerFECompositionStateOne.compositionType = hal::Composition::DEVICE; + setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + layerFECompositionStateOne); + LayerState layerStateOne(&outputLayerOne); + + Plan plan; + plan.addLayerType(hal::Composition::DEVICE); + + Predictor predictor; + + NonBufferHash hash = getNonBufferHash({&layerStateOne}); + + predictor.recordResult(std::nullopt, hash, {&layerStateOne}, false, plan); + + auto predictedPlan = predictor.getPredictedPlan({}, hash); + EXPECT_TRUE(predictedPlan); + Predictor::PredictedPlan expectedPlan{hash, plan, Prediction::Type::Exact}; + EXPECT_EQ(expectedPlan, predictedPlan); +} + +TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) { + 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); + + Plan plan; + plan.addLayerType(hal::Composition::DEVICE); + + Predictor predictor; + + NonBufferHash hashOne = getNonBufferHash({&layerStateOne}); + NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo}); + + predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan); + + auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo); + EXPECT_TRUE(predictedPlan); + Predictor::PredictedPlan expectedPlan{hashOne, plan, Prediction::Type::Approximate}; + EXPECT_EQ(expectedPlan, predictedPlan); +} + +TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) { + 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); + + Plan plan; + plan.addLayerType(hal::Composition::DEVICE); + + Predictor predictor; + + NonBufferHash hashOne = getNonBufferHash({&layerStateOne}); + NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo}); + + predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan); + + auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo); + ASSERT_TRUE(predictedPlan); + EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type); + + Plan planTwo; + planTwo.addLayerType(hal::Composition::CLIENT); + predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo); + // Now trying to retrieve the predicted plan again returns a nullopt instead. + // TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this. + // One of the implications around this implementation is that if we miss a prediction then we + // can never actually correct our mistake if we see the same layer stack again, which doesn't + // seem robust. + auto predictedPlanTwo = predictor.getPredictedPlan({&layerStateTwo}, hashTwo); + EXPECT_FALSE(predictedPlanTwo); +} + } // namespace } // namespace android::compositionengine::impl::planner
\ No newline at end of file |