diff options
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfo.cpp | 43 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfo.h | 7 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp | 86 |
3 files changed, 125 insertions, 11 deletions
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index f9c9023bd9..0142ccd93f 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -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 { @@ -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()}; @@ -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); } diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index a5ffbbecb5..93485be0a4 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -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; @@ -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 diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 979924af58..8397f8d0ce 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -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(); @@ -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; @@ -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(); |