Loading services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h +33 −2 Original line number Diff line number Diff line Loading @@ -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; }; Loading Loading @@ -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)) {} Loading Loading @@ -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); Loading Loading @@ -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 services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp +203 −11 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 = Loading @@ -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) { Loading Loading @@ -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 Loading
services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h +33 −2 Original line number Diff line number Diff line Loading @@ -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; }; Loading Loading @@ -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)) {} Loading Loading @@ -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); Loading Loading @@ -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
services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp +203 −11 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 = Loading @@ -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) { Loading Loading @@ -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