diff options
| author | 2020-05-18 21:05:01 +0000 | |
|---|---|---|
| committer | 2020-05-18 21:05:01 +0000 | |
| commit | 51a1625ac047e31f9025130507b69bf5a9112c03 (patch) | |
| tree | 8f611fcfeeef09033ff6a7e14e0d1732b63cfa05 | |
| parent | 9b5bf0f42d57387a3451aa9eb9bfb1075fcb91a4 (diff) | |
| parent | 1adbb727595c6da51bb088e4ce3f8070d9b32808 (diff) | |
Merge "SurfaceFlinger: more aggressive infrequent layer detection" into rvc-dev
7 files changed, 75 insertions, 94 deletions
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index e6c5cc9700..120a60f8d8 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -90,7 +90,7 @@ LayerHistoryV2::~LayerHistoryV2() = default; void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate); - auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type); + auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type); std::lock_guard lock(mLock); mLayerInfos.emplace_back(layer, std::move(info)); } diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp index b7d0bdd101..255eac6efc 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -27,8 +27,10 @@ namespace android::scheduler { -LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) - : mHighRefreshRatePeriod(highRefreshRatePeriod), +LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, + LayerHistory::LayerVoteType defaultVote) + : mName(name), + mHighRefreshRatePeriod(highRefreshRatePeriod), mDefaultVote(defaultVote), mLayerVote({defaultVote, 0.0f}) {} @@ -45,42 +47,23 @@ void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) { } } -bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { - return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>( - mFrameTimeValidSince.time_since_epoch()) - .count(); -} - bool LayerInfoV2::isFrequent(nsecs_t now) const { - // Find the first valid frame time - auto it = mFrameTimes.begin(); - for (; it != mFrameTimes.end(); ++it) { - if (isFrameTimeValid(*it)) { - break; + for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) { + if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) { + ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(), + (now - mFrameTimes.back().queueTime) / 1e6f); + return false; } - } - // If we know nothing about this layer we consider it as frequent as it might be the start - // of an animation. - if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) { - return true; - } - - // Find the first active frame - for (; it != mFrameTimes.end(); ++it) { - if (it->queueTime >= getActiveLayerThreshold(now)) { - break; + const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1); + if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) { + ALOGV("%s frequent (burst of %zu frames", mName.c_str(), numFrames); + return true; } } - const auto numFrames = std::distance(it, mFrameTimes.end()); - if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) { - return false; - } - - // Layer is considered frequent if the average frame rate is higher than the threshold - const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; - return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER; + ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size()); + return false; } bool LayerInfoV2::hasEnoughDataForHeuristic() const { @@ -89,10 +72,6 @@ bool LayerInfoV2::hasEnoughDataForHeuristic() const { return false; } - if (!isFrameTimeValid(mFrameTimes.front())) { - return false; - } - if (mFrameTimes.size() < HISTORY_SIZE && mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) { return false; @@ -167,18 +146,22 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() { std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { + ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); return {mLayerVote.type, mLayerVote.fps}; } if (!isFrequent(now)) { + ALOGV("%s is infrequent", mName.c_str()); return {LayerHistory::LayerVoteType::Min, 0}; } auto refreshRate = calculateRefreshRateIfPossible(); if (refreshRate.has_value()) { + ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; } + ALOGV("%s Max (can't resolve refresh rate", mName.c_str()); return {LayerHistory::LayerVoteType::Max, 0}; } diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h index e36b7f709e..97c7017648 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -54,7 +54,8 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: - LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); + LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, + LayerHistory::LayerVoteType defaultVote); LayerInfoV2(const LayerInfo&) = delete; LayerInfoV2& operator=(const LayerInfoV2&) = delete; @@ -83,11 +84,7 @@ public: nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } void clearHistory() { - // Mark mFrameTimeValidSince to now to ignore all previous frame times. - // We are not deleting the old frame to keep track of whether we should treat the first - // buffer as Max as we don't know anything about this layer or Min as this layer is - // posting infrequent updates. - mFrameTimeValidSince = std::chrono::steady_clock::now(); + mFrameTimes.clear(); mLastReportedRefreshRate = 0.0f; } @@ -101,7 +98,8 @@ private: bool isFrequent(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; std::optional<float> calculateRefreshRateIfPossible(); - bool isFrameTimeValid(const FrameTimeData&) const; + + const std::string mName; // Used for sanitizing the heuristic data const nsecs_t mHighRefreshRatePeriod; @@ -118,8 +116,6 @@ private: } mLayerVote; std::deque<FrameTimeData> mFrameTimes; - std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince = - std::chrono::steady_clock::now(); static constexpr size_t HISTORY_SIZE = 90; static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; }; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 8d958df572..69b1a3c261 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -103,7 +103,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); - *touchConsidered = false; + if (touchConsidered) *touchConsidered = false; std::lock_guard lock(mLock); int noVoteLayers = 0; @@ -131,7 +131,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) { - *touchConsidered = true; + ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); + if (touchConsidered) *touchConsidered = true; return getMaxRefreshRateByPolicyLocked(); } @@ -145,6 +146,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Only if all layers want Min we should return Min if (noVoteLayers + minVoteLayers == layers.size()) { + ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); return getMinRefreshRateByPolicyLocked(); } @@ -243,9 +245,11 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( return 1.0f / iter; }(); - ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f", - layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), - layerScore); + ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), + layer.vote == LayerVoteType::ExplicitExactOrMultiple + ? "ExplicitExactOrMultiple" + : "Heuristic", + weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } @@ -266,7 +270,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); if (touchActive && explicitDefaultVoteLayers == 0 && bestRefreshRate->fps < touchRefreshRate.fps) { - *touchConsidered = true; + if (touchConsidered) *touchConsidered = true; + ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index f87c1f8131..00f87bbf59 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -526,7 +526,9 @@ void Scheduler::idleTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) { const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; - handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */); + if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) { + mLayerHistory->clear(); + } ATRACE_INT("TouchState", static_cast<int>(touch)); } @@ -549,18 +551,19 @@ void Scheduler::dump(std::string& result) const { } template <class T> -void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { +bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { ConfigEvent event = ConfigEvent::None; HwcConfigIndexType newConfigId; + bool touchConsidered = false; { std::lock_guard<std::mutex> lock(mFeatureStateLock); if (*currentState == newState) { - return; + return touchConsidered; } *currentState = newState; - newConfigId = calculateRefreshRateConfigIndexType(); + newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered); if (mFeatures.configId == newConfigId) { - return; + return touchConsidered; } mFeatures.configId = newConfigId; if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) { @@ -569,10 +572,12 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO } const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, event); + return touchConsidered; } -HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { +HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) { ATRACE_CALL(); + if (touchConsidered) *touchConsidered = false; // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. @@ -607,18 +612,9 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { .getConfigId(); } - bool touchConsidered; - const auto& ret = mRefreshRateConfigs - .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, - &touchConsidered) - .getConfigId(); - if (touchConsidered) { - // Clear layer history if refresh rate was selected based on touch to allow - // the hueristic to pick up with the new rate. - mLayerHistory->clear(); - } - - return ret; + return mRefreshRateConfigs + .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered) + .getConfigId(); } std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 4a0280fe1e..6eabfd2e47 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -177,14 +177,15 @@ private: // handles various timer features to change the refresh rate. template <class T> - void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); + bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); void setVsyncPeriod(nsecs_t period); // This function checks whether individual features that are affecting the refresh rate // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType // for the suggested refresh rate. - HwcConfigIndexType calculateRefreshRateConfigIndexType() REQUIRES(mFeatureStateLock); + HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr) + REQUIRES(mFeatureStateLock); // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index 431cf0f4b3..d55648ae34 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -103,12 +103,22 @@ TEST_F(LayerHistoryTestV2, oneLayer) { EXPECT_TRUE(history().summarize(time).empty()); EXPECT_EQ(0, activeLayerCount()); + // The first few updates are considered infrequent + for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { + history().record(layer.get(), 0, time); + ASSERT_EQ(1, history().summarize(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + } + // Max returned if active layers have insufficient history. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { history().record(layer.get(), 0, time); ASSERT_EQ(1, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); } // Max is returned since we have enough history but there is no timestamp votes. @@ -117,6 +127,7 @@ TEST_F(LayerHistoryTestV2, oneLayer) { ASSERT_EQ(1, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(1, frequentLayerCount(time)); } } @@ -134,7 +145,7 @@ TEST_F(LayerHistoryTestV2, oneInvisibleLayer) { auto summary = history().summarize(time); ASSERT_EQ(1, history().summarize(time).size()); // Layer is still considered inactive so we expect to get Min - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false)); @@ -464,28 +475,15 @@ TEST_F(LayerHistoryTestV2, inactiveLayers) { nsecs_t time = systemTime(); - // the very first updates makes the layer frequent + // The first few updates are considered infrequent for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer.get(), time, time); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - - EXPECT_EQ(1, layerCount()); + history().record(layer.get(), 0, time); ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); + EXPECT_EQ(0, frequentLayerCount(time)); } - // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent - history().record(layer.get(), time, time); - time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - // advance the time for the previous frame to be inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); @@ -528,9 +526,11 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) { nsecs_t time = systemTime(); - // Post a buffer to the layers to make them active - history().record(explicitVisiblelayer.get(), time, time); - history().record(explicitInvisiblelayer.get(), time, time); + // Post a few buffers to the layers to make them active + for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { + history().record(explicitVisiblelayer.get(), time, time); + history().record(explicitInvisiblelayer.get(), time, time); + } EXPECT_EQ(2, layerCount()); ASSERT_EQ(1, history().summarize(time).size()); |