Loading services/surfaceflinger/Scheduler/LayerInfo.cpp +37 −6 Original line number Diff line number Diff line Loading @@ -77,12 +77,43 @@ bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const { bool LayerInfo::isFrequent(nsecs_t now) const { using fps_approx_ops::operator>=; // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. // If we know nothing about this layer (e.g. after touch event), // we consider it as frequent as it might be the start of an animation. if (mFrameTimes.size() < kFrequentLayerWindowSize) { return true; } return getFps(now) >= kMinFpsForFrequentLayer; // Non-active layers are also infrequent if (mLastUpdatedTime < getActiveLayerThreshold(now)) { return false; } // We check whether we can classify this layer as frequent or infrequent: // - frequent: a layer posted kFrequentLayerWindowSize within // kMaxPeriodForFrequentLayerNs of each other. // - infrequent: a layer posted kFrequentLayerWindowSize with longer // gaps than kFrequentLayerWindowSize. // If we can't determine the layer classification yet, we return the last // classification. bool isFrequent = true; bool isInfrequent = true; const auto n = mFrameTimes.size() - 1; for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) { if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime < kMaxPeriodForFrequentLayerNs.count()) { isInfrequent = false; } else { isFrequent = false; } } if (isFrequent || isInfrequent) { return isFrequent; } // If we can't determine whether the layer is frequent or not, we return // the last known classification. return !mLastRefreshRate.infrequent; } Fps LayerInfo::getFps(nsecs_t now) const { Loading Loading @@ -235,14 +266,14 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& se if (isAnimating(now)) { ATRACE_FORMAT_INSTANT("animating"); ALOGV("%s is animating", mName.c_str()); mLastRefreshRate.animatingOrInfrequent = true; mLastRefreshRate.animating = true; return {LayerHistory::LayerVoteType::Max, Fps()}; } if (!isFrequent(now)) { ATRACE_FORMAT_INSTANT("infrequent"); ALOGV("%s is infrequent", mName.c_str()); mLastRefreshRate.animatingOrInfrequent = true; mLastRefreshRate.infrequent = true; // Infrequent layers vote for mininal refresh rate for // battery saving purposes and also to prevent b/135718869. return {LayerHistory::LayerVoteType::Min, Fps()}; Loading @@ -251,7 +282,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& se // If the layer was previously tagged as animating or infrequent, we clear // the history as it is likely the layer just changed its behavior // and we should not look at stale data if (mLastRefreshRate.animatingOrInfrequent) { if (mLastRefreshRate.animating || mLastRefreshRate.infrequent) { clearHistory(now); } Loading services/surfaceflinger/Scheduler/LayerInfo.h +5 −2 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ class LayerInfo { // Layer is considered frequent if the earliest value in the window of most recent present times // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. static constexpr size_t kFrequentLayerWindowSize = 3; static constexpr size_t kFrequentLayerWindowSize = 4; static constexpr Fps kMinFpsForFrequentLayer = 10_Hz; static constexpr auto kMaxPeriodForFrequentLayerNs = std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms; Loading Loading @@ -214,7 +214,10 @@ private: Fps reported; // Whether the last reported rate for LayerInfo::getRefreshRate() // was due to animation or infrequent updates bool animatingOrInfrequent = false; bool animating = false; // Whether the last reported rate for LayerInfo::getRefreshRate() // was due to infrequent updates bool infrequent = false; }; // Class to store past calculated refresh rate and determine whether Loading services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +83 −3 Original line number Diff line number Diff line Loading @@ -51,8 +51,6 @@ protected: static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs; static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize; static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION; static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION = LayerInfo::RefreshRateHistory::HISTORY_DURATION; static constexpr Fps LO_FPS = 30_Hz; static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs(); Loading Loading @@ -607,7 +605,7 @@ TEST_F(LayerHistoryTest, inactiveLayers) { // advance the time for the previous frame to be inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); // Now event if we post a quick few frame we should stay infrequent // Now even if we post a quick few frame we should stay infrequent for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; Loading Loading @@ -706,6 +704,88 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(1, animatingLayerCount(time)); } TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); nsecs_t time = systemTime(); EXPECT_EQ(1, layerCount()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // Fill up the window with frequent updates for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += (60_Hz).getPeriodNsecs(); EXPECT_EQ(1, layerCount()); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); } // posting a buffer after long inactivity should retain the layer as active time += std::chrono::nanoseconds(3s).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting more infrequent buffer should make the layer infrequent time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting another buffer should keep the layer infrequent history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting more buffers would mean starting of an animation, so making the layer frequent history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting a buffer after long inactivity should retain the layer as active time += std::chrono::nanoseconds(3s).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting another buffer should keep the layer frequent time += (60_Hz).getPeriodNsecs(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); } TEST_F(LayerHistoryTest, getFramerate) { auto layer = createLayer(); Loading Loading
services/surfaceflinger/Scheduler/LayerInfo.cpp +37 −6 Original line number Diff line number Diff line Loading @@ -77,12 +77,43 @@ bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const { bool LayerInfo::isFrequent(nsecs_t now) const { using fps_approx_ops::operator>=; // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. // If we know nothing about this layer (e.g. after touch event), // we consider it as frequent as it might be the start of an animation. if (mFrameTimes.size() < kFrequentLayerWindowSize) { return true; } return getFps(now) >= kMinFpsForFrequentLayer; // Non-active layers are also infrequent if (mLastUpdatedTime < getActiveLayerThreshold(now)) { return false; } // We check whether we can classify this layer as frequent or infrequent: // - frequent: a layer posted kFrequentLayerWindowSize within // kMaxPeriodForFrequentLayerNs of each other. // - infrequent: a layer posted kFrequentLayerWindowSize with longer // gaps than kFrequentLayerWindowSize. // If we can't determine the layer classification yet, we return the last // classification. bool isFrequent = true; bool isInfrequent = true; const auto n = mFrameTimes.size() - 1; for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) { if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime < kMaxPeriodForFrequentLayerNs.count()) { isInfrequent = false; } else { isFrequent = false; } } if (isFrequent || isInfrequent) { return isFrequent; } // If we can't determine whether the layer is frequent or not, we return // the last known classification. return !mLastRefreshRate.infrequent; } Fps LayerInfo::getFps(nsecs_t now) const { Loading Loading @@ -235,14 +266,14 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& se if (isAnimating(now)) { ATRACE_FORMAT_INSTANT("animating"); ALOGV("%s is animating", mName.c_str()); mLastRefreshRate.animatingOrInfrequent = true; mLastRefreshRate.animating = true; return {LayerHistory::LayerVoteType::Max, Fps()}; } if (!isFrequent(now)) { ATRACE_FORMAT_INSTANT("infrequent"); ALOGV("%s is infrequent", mName.c_str()); mLastRefreshRate.animatingOrInfrequent = true; mLastRefreshRate.infrequent = true; // Infrequent layers vote for mininal refresh rate for // battery saving purposes and also to prevent b/135718869. return {LayerHistory::LayerVoteType::Min, Fps()}; Loading @@ -251,7 +282,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& se // If the layer was previously tagged as animating or infrequent, we clear // the history as it is likely the layer just changed its behavior // and we should not look at stale data if (mLastRefreshRate.animatingOrInfrequent) { if (mLastRefreshRate.animating || mLastRefreshRate.infrequent) { clearHistory(now); } Loading
services/surfaceflinger/Scheduler/LayerInfo.h +5 −2 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ class LayerInfo { // Layer is considered frequent if the earliest value in the window of most recent present times // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. static constexpr size_t kFrequentLayerWindowSize = 3; static constexpr size_t kFrequentLayerWindowSize = 4; static constexpr Fps kMinFpsForFrequentLayer = 10_Hz; static constexpr auto kMaxPeriodForFrequentLayerNs = std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms; Loading Loading @@ -214,7 +214,10 @@ private: Fps reported; // Whether the last reported rate for LayerInfo::getRefreshRate() // was due to animation or infrequent updates bool animatingOrInfrequent = false; bool animating = false; // Whether the last reported rate for LayerInfo::getRefreshRate() // was due to infrequent updates bool infrequent = false; }; // Class to store past calculated refresh rate and determine whether Loading
services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +83 −3 Original line number Diff line number Diff line Loading @@ -51,8 +51,6 @@ protected: static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs; static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize; static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION; static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION = LayerInfo::RefreshRateHistory::HISTORY_DURATION; static constexpr Fps LO_FPS = 30_Hz; static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs(); Loading Loading @@ -607,7 +605,7 @@ TEST_F(LayerHistoryTest, inactiveLayers) { // advance the time for the previous frame to be inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); // Now event if we post a quick few frame we should stay infrequent // Now even if we post a quick few frame we should stay infrequent for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; Loading Loading @@ -706,6 +704,88 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(1, animatingLayerCount(time)); } TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); nsecs_t time = systemTime(); EXPECT_EQ(1, layerCount()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // Fill up the window with frequent updates for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += (60_Hz).getPeriodNsecs(); EXPECT_EQ(1, layerCount()); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); } // posting a buffer after long inactivity should retain the layer as active time += std::chrono::nanoseconds(3s).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting more infrequent buffer should make the layer infrequent time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting another buffer should keep the layer infrequent history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting more buffers would mean starting of an animation, so making the layer frequent history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting a buffer after long inactivity should retain the layer as active time += std::chrono::nanoseconds(3s).count(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); // posting another buffer should keep the layer frequent time += (60_Hz).getPeriodNsecs(); history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, animatingLayerCount(time)); } TEST_F(LayerHistoryTest, getFramerate) { auto layer = createLayer(); Loading