From b1b9d41c73cfb388a81a2bc45b0c6708e2e627f7 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Mon, 1 Jun 2020 19:53:52 -0700 Subject: SurfaceFlinger: define a known frame rates list Keep a list of known frame rates that would be used when we calculated heuristically the frame rate of a layer. This keeps the signal to the algorithm that chooses the refresh rate steady and avoid strange frame rates like 57.2 due to inconsistent presentation timestamps. Bug: 157540021 Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest Change-Id: I97a24b74605256646e9b8444bd9f3818fe0a4a2a --- services/surfaceflinger/Scheduler/LayerHistory.h | 2 +- .../surfaceflinger/Scheduler/LayerHistoryV2.cpp | 7 +- services/surfaceflinger/Scheduler/LayerInfoV2.cpp | 17 +++-- services/surfaceflinger/Scheduler/LayerInfoV2.h | 17 ++++- .../Scheduler/RefreshRateConfigs.cpp | 41 ++++++++++- .../surfaceflinger/Scheduler/RefreshRateConfigs.h | 14 +++- services/surfaceflinger/Scheduler/Scheduler.cpp | 2 +- .../tests/unittests/LayerHistoryTestV2.cpp | 38 ++++++++++ .../tests/unittests/RefreshRateConfigsTest.cpp | 81 ++++++++++++++++++++-- .../tests/unittests/TestableScheduler.h | 4 +- 10 files changed, 201 insertions(+), 22 deletions(-) diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index acd76b0558..228b8a06af 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -136,7 +136,7 @@ private: class LayerHistoryV2 : public android::scheduler::LayerHistory { public: - LayerHistoryV2(); + LayerHistoryV2(const scheduler::RefreshRateConfigs&); virtual ~LayerHistoryV2(); // Layers are unregistered when the weak reference expires. diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index 316000c4d9..6216f6e3e2 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -87,8 +87,11 @@ void trace(const wp& weak, LayerHistory::LayerVoteType type, int fps) { } } // namespace -LayerHistoryV2::LayerHistoryV2() - : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {} +LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs) + : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) { + LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs); +} + LayerHistoryV2::~LayerHistoryV2() = default; void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp index 82da00785a..f4bbc376e0 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -27,6 +27,8 @@ namespace android::scheduler { +const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr; + LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) : mName(name), @@ -168,7 +170,6 @@ bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPres std::optional LayerInfoV2::calculateRefreshRateIfPossible() { static constexpr float MARGIN = 1.0f; // 1Hz - if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); return std::nullopt; @@ -177,7 +178,7 @@ std::optional LayerInfoV2::calculateRefreshRateIfPossible() { const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime(); // If there are no presentation timestamps provided we can't calculate the refresh rate - if (missingPresentTime && mLastReportedRefreshRate == 0) { + if (missingPresentTime && mLastRefreshRate.reported == 0) { return std::nullopt; } @@ -186,12 +187,16 @@ std::optional LayerInfoV2::calculateRefreshRateIfPossible() { } const auto refreshRate = 1e9f / averageFrameTime; - if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) { - mLastReportedRefreshRate = refreshRate; + const auto knownRefreshRate = sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate); + if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN && + mLastRefreshRate.reported != knownRefreshRate) { + mLastRefreshRate.calculated = refreshRate; + mLastRefreshRate.reported = knownRefreshRate; } - ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate); - return mLastReportedRefreshRate; + ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(), refreshRate, + mLastRefreshRate.reported); + return mLastRefreshRate.reported; } std::pair LayerInfoV2::getRefreshRate(nsecs_t now) { diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h index 9e6783f854..c434963189 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -56,6 +56,10 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: + static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) { + sRefreshRateConfigs = &refreshRateConfigs; + } + LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); @@ -93,7 +97,7 @@ public: // posting infrequent updates. const auto timePoint = std::chrono::nanoseconds(now); mFrameTimeValidSince = std::chrono::time_point(timePoint); - mLastReportedRefreshRate = 0.0f; + mLastRefreshRate = {}; } void clearHistory(nsecs_t now) { @@ -127,7 +131,13 @@ private: nsecs_t mLastAnimationTime = 0; - float mLastReportedRefreshRate = 0.0f; + // Holds information about the calculated and reported refresh rate + struct RefreshRateHeuristicData { + float calculated = 0.0f; // Rate calculated on the layer + float reported = 0.0f; // Last reported rate for LayerInfoV2::getRefreshRate() + }; + + RefreshRateHeuristicData mLastRefreshRate; // Holds information about the layer vote struct { @@ -140,6 +150,9 @@ private: std::chrono::steady_clock::now(); static constexpr size_t HISTORY_SIZE = 90; static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; + + // Shared for all LayerInfo instances + static const RefreshRateConfigs* sRefreshRateConfigs; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 3c8bd68145..ea27955287 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -242,7 +242,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || layer.vote == LayerVoteType::Heuristic) { - const auto layerScore = [&]() { + const auto layerScore = [&] { // Calculate how many display vsyncs we need to present a single frame for this // layer const auto [displayFramesQuot, displayFramesRem] = @@ -384,7 +384,8 @@ void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) { RefreshRateConfigs::RefreshRateConfigs( const std::vector>& configs, - HwcConfigIndexType currentConfigId) { + HwcConfigIndexType currentConfigId) + : mKnownFrameRates(constructKnownFrameRates(configs)) { LOG_ALWAYS_FATAL_IF(configs.empty()); LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size()); @@ -544,4 +545,40 @@ void RefreshRateConfigs::constructAvailableRefreshRates() { &mAppRequestRefreshRates); } +std::vector RefreshRateConfigs::constructKnownFrameRates( + const std::vector>& configs) { + std::vector knownFrameRates = {24.0f, 30.0f, 45.0f, 60.0f, 72.0f}; + knownFrameRates.reserve(knownFrameRates.size() + configs.size()); + + // Add all supported refresh rates to the set + for (const auto& config : configs) { + const auto refreshRate = 1e9f / config->getVsyncPeriod(); + knownFrameRates.emplace_back(refreshRate); + } + + // Sort and remove duplicates + const auto frameRatesEqual = [](float a, float b) { return std::abs(a - b) <= 0.01f; }; + std::sort(knownFrameRates.begin(), knownFrameRates.end()); + knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(), + frameRatesEqual), + knownFrameRates.end()); + return knownFrameRates; +} + +float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const { + if (frameRate <= *mKnownFrameRates.begin()) { + return *mKnownFrameRates.begin(); + } + + if (frameRate >= *std::prev(mKnownFrameRates.end())) { + return *std::prev(mKnownFrameRates.end()); + } + + auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate); + + const auto distance1 = std::abs(frameRate - *lowerBound); + const auto distance2 = std::abs(frameRate - *std::prev(lowerBound)); + return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound); +} + } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index ff1eabdfc8..88e4eb5a5b 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -29,7 +29,6 @@ #include "Scheduler/StrongTyping.h" namespace android::scheduler { -class RefreshRateConfigsTest; using namespace std::chrono_literals; @@ -87,7 +86,7 @@ public: private: friend RefreshRateConfigs; - friend RefreshRateConfigsTest; + friend class RefreshRateConfigsTest; // The tolerance within which we consider FPS approximately equals. static constexpr float FPS_EPSILON = 0.001f; @@ -258,11 +257,18 @@ public: // Returns a string that represents the layer vote type static std::string layerVoteTypeString(LayerVoteType vote); + // Returns a known frame rate that is the closest to frameRate + float findClosestKnownFrameRate(float frameRate) const; + RefreshRateConfigs(const std::vector>& configs, HwcConfigIndexType currentConfigId); private: + friend class RefreshRateConfigsTest; + void constructAvailableRefreshRates() REQUIRES(mLock); + static std::vector constructKnownFrameRates( + const std::vector>& configs); void getSortedRefreshRateList( const std::function& shouldAddRefreshRate, @@ -320,6 +326,10 @@ private: const RefreshRate* mMaxSupportedRefreshRate; mutable std::mutex mLock; + + // A sorted list of known frame rates that a Heuristic layer will choose + // from based on the closest value. + const std::vector mKnownFrameRates; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index e250bbf064..bfa749344a 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -111,7 +111,7 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, using namespace sysprop; if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique(); + mLayerHistory = std::make_unique(refreshRateConfig); } else { mLayerHistory = std::make_unique(); } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index f376b4a3c1..2d3b01f6bd 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -84,6 +84,22 @@ protected: return sp(new mock::MockLayer(mFlinger.flinger(), std::move(name))); } + void recordFramesAndExpect(const sp& layer, float frameRate, + float desiredRefreshRate) { + nsecs_t time = systemTime(); + const nsecs_t framePeriod = static_cast(1e9f / frameRate); + + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += framePeriod; + } + + ASSERT_EQ(1, history().summarize(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); + EXPECT_FLOAT_EQ(desiredRefreshRate, history().summarize(time)[0].desiredRefreshRate) + << "Frame rate is " << frameRate; + } + Hwc2::mock::Display mDisplay; RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0) .setVsyncPeriod(int32_t(LO_FPS_PERIOD)) @@ -600,6 +616,28 @@ TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) { EXPECT_EQ(1, animatingLayerCount(time)); } +TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) { + const auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + + for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) { + recordFramesAndExpect(layer, fps, 60.0f); + } +} + +TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) { + const auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + + recordFramesAndExpect(layer, 27.10f, 30.0f); + recordFramesAndExpect(layer, 26.50f, 30.0f); + recordFramesAndExpect(layer, 25.90f, 24.0f); + recordFramesAndExpect(layer, 26.50f, 24.0f); + recordFramesAndExpect(layer, 27.10f, 30.0f); +} + class LayerHistoryTestV2Parameterized : public LayerHistoryTestV2, public testing::WithParamInterface {}; diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index c919e93795..4fa84555b7 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -43,6 +43,14 @@ protected: RefreshRateConfigsTest(); ~RefreshRateConfigsTest(); + float findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, float frameRate) { + return refreshRateConfigs.findClosestKnownFrameRate(frameRate); + } + + std::vector getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) { + return refreshRateConfigs.mKnownFrameRates; + } + // Test config IDs static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0); static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1); @@ -902,8 +910,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) { const auto& refreshRate = refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false, &ignored); - printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str()); - EXPECT_EQ(mExpected60Config, refreshRate); + EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName(); } } @@ -989,8 +996,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) { const auto& refreshRate = refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false, &ignored); - printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str()); - EXPECT_EQ(mExpected90Config, refreshRate); + EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName(); } } @@ -1456,6 +1462,73 @@ TEST_F(RefreshRateConfigsTest, idle) { .getConfigId()); } +TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) { + auto refreshRateConfigs = + std::make_unique(m60_90Device, + /*currentConfigId=*/HWC_CONFIG_ID_60); + + for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) { + const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, fps); + float expectedFrameRate; + if (fps < 26.91f) { + expectedFrameRate = 24.0f; + } else if (fps < 37.51f) { + expectedFrameRate = 30.0f; + } else if (fps < 52.51f) { + expectedFrameRate = 45.0f; + } else if (fps < 66.01f) { + expectedFrameRate = 60.0f; + } else if (fps < 81.01f) { + expectedFrameRate = 72.0f; + } else { + expectedFrameRate = 90.0f; + } + EXPECT_FLOAT_EQ(expectedFrameRate, knownFrameRate) + << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate; + } +} + +TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { + bool ignored; + auto refreshRateConfigs = + std::make_unique(m60_90Device, + /*currentConfigId=*/HWC_CONFIG_ID_60); + + struct ExpectedRate { + float rate; + const RefreshRate& expected; + }; + + /* clang-format off */ + std::vector knownFrameRatesExpectations = { + {24.0f, mExpected60Config}, + {30.0f, mExpected60Config}, + {45.0f, mExpected90Config}, + {60.0f, mExpected60Config}, + {72.0f, mExpected90Config}, + {90.0f, mExpected90Config}, + }; + /* clang-format on */ + + // Make sure the test tests all the known frame rate + const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs); + const auto equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(), + knownFrameRatesExpectations.begin(), + [](float a, const ExpectedRate& b) { return a == b.rate; }); + EXPECT_TRUE(equal); + + auto layers = std::vector{LayerRequirement{.weight = 1.0f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::Heuristic; + for (const auto& expectedRate : knownFrameRatesExpectations) { + layer.desiredRefreshRate = expectedRate.rate; + const auto& refreshRate = refreshRateConfigs->getBestRefreshRate(layers, + /*touchActive*/ false, + /*idle*/ false, &ignored); + EXPECT_EQ(expectedRate.expected, refreshRate); + } +} + } // namespace } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 41b5d49bf8..d5ecae81cd 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -31,7 +31,7 @@ public: TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) { if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique(); + mLayerHistory = std::make_unique(configs); } else { mLayerHistory = std::make_unique(); } @@ -43,7 +43,7 @@ public: : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this, useContentDetectionV2, true) { if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique(); + mLayerHistory = std::make_unique(configs); } else { mLayerHistory = std::make_unique(); } -- cgit v1.2.3-59-g8ed1b From 0ccd79b10b37de17c78f9ad91ed06a29502d7c6d Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Wed, 10 Jun 2020 10:11:17 -0700 Subject: SurfaceFlinger: stabilize heuristic refresh rate calculation When calculating layer's refresh rate heuristically, also store a history of the last refresh rate calculated. We use this list to determine how consistent is the refresh rate. If the refresh rate is not consistent, then we keep reporting the last consistent refresh rate and wait for the refresh rate to stabilize again. Test: Play at 60fps video in XPlayer Bug: 157540021 Change-Id: If3b525820d298cc5835dddf73f327501c8a18964 --- .../surfaceflinger/Scheduler/LayerHistoryV2.cpp | 35 ++--- services/surfaceflinger/Scheduler/LayerInfo.h | 6 +- services/surfaceflinger/Scheduler/LayerInfoV2.cpp | 169 +++++++++++++++------ services/surfaceflinger/Scheduler/LayerInfoV2.h | 96 +++++++++--- .../tests/unittests/LayerHistoryTestV2.cpp | 131 ++++++++++------ 5 files changed, 297 insertions(+), 140 deletions(-) diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index 6216f6e3e2..ee612b0732 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -58,30 +58,21 @@ bool useFrameRatePriority() { return atoi(value); } -void trace(const wp& weak, LayerHistory::LayerVoteType type, int fps) { +void trace(const wp& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type, + int fps) { const auto layer = weak.promote(); if (!layer) return; - const auto makeTag = [layer](LayerHistory::LayerVoteType vote) { - return "LFPS " + RefreshRateConfigs::layerVoteTypeString(vote) + " " + layer->getName(); + const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) { + ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0); }; - const auto noVoteTag = makeTag(LayerHistory::LayerVoteType::NoVote); - const auto heuristicVoteTag = makeTag(LayerHistory::LayerVoteType::Heuristic); - const auto explicitDefaultVoteTag = makeTag(LayerHistory::LayerVoteType::ExplicitDefault); - const auto explicitExactOrMultipleVoteTag = - makeTag(LayerHistory::LayerVoteType::ExplicitExactOrMultiple); - const auto minVoteTag = makeTag(LayerHistory::LayerVoteType::Min); - const auto maxVoteTag = makeTag(LayerHistory::LayerVoteType::Max); - - ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0); - ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0); - ATRACE_INT(explicitDefaultVoteTag.c_str(), - type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0); - ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(), - type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0); - ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0); - ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0); + traceType(LayerHistory::LayerVoteType::NoVote, 1); + traceType(LayerHistory::LayerVoteType::Heuristic, fps); + traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps); + traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps); + traceType(LayerHistory::LayerVoteType::Min, 1); + traceType(LayerHistory::LayerVoteType::Max, 1); ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps); } @@ -89,6 +80,7 @@ void trace(const wp& weak, LayerHistory::LayerVoteType type, int fps) { LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs) : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) { + LayerInfoV2::setTraceEnabled(mTraceEnabled); LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs); } @@ -154,7 +146,7 @@ LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) { summary.push_back({strong->getName(), type, refreshRate, weight}); if (CC_UNLIKELY(mTraceEnabled)) { - trace(layer, type, static_cast(std::round(refreshRate))); + trace(layer, *info, type, static_cast(std::round(refreshRate))); } } @@ -193,7 +185,7 @@ void LayerHistoryV2::partitionLayers(nsecs_t now) { } if (CC_UNLIKELY(mTraceEnabled)) { - trace(weak, LayerHistory::LayerVoteType::NoVote, 0); + trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0); } info->onLayerInactive(now); @@ -220,4 +212,5 @@ void LayerHistoryV2::clear() { info->clearHistory(systemTime()); } } + } // namespace android::scheduler::impl diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index cb81ca2840..820624b905 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -98,9 +98,9 @@ class LayerInfo { return false; } - // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates + // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates if (mElements.size() < HISTORY_SIZE && - mElements.back() - mElements.front() < HISTORY_TIME.count()) { + mElements.back() - mElements.front() < HISTORY_DURATION.count()) { return false; } @@ -124,7 +124,7 @@ class LayerInfo { private: std::deque mElements; - static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; + static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s; }; friend class LayerHistoryTest; diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp index f4bbc376e0..44f20d0063 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -15,26 +15,31 @@ */ // #define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "LayerInfoV2.h" #include #include +#include +#include + #undef LOG_TAG #define LOG_TAG "LayerInfoV2" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS namespace android::scheduler { const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr; +bool LayerInfoV2::sTraceEnabled = false; LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) : mName(name), mHighRefreshRatePeriod(highRefreshRatePeriod), mDefaultVote(defaultVote), - mLayerVote({defaultVote, 0.0f}) {} + mLayerVote({defaultVote, 0.0f}), + mRefreshRateHistory(name) {} void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType, bool pendingConfigChange) { @@ -94,24 +99,28 @@ bool LayerInfoV2::isAnimating(nsecs_t now) const { } bool LayerInfoV2::hasEnoughDataForHeuristic() const { - // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates + // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates if (mFrameTimes.size() < 2) { + ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size()); return false; } if (!isFrameTimeValid(mFrameTimes.front())) { + ALOGV("stale frames still captured"); return false; } - if (mFrameTimes.size() < HISTORY_SIZE && - mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) { + const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime; + if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) { + ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(), + totalDuration / 1e9f); return false; } return true; } -std::pair LayerInfoV2::calculateAverageFrameTime() const { +std::optional LayerInfoV2::calculateAverageFrameTime() const { nsecs_t totalPresentTimeDeltas = 0; nsecs_t totalQueueTimeDeltas = 0; bool missingPresentTime = false; @@ -119,15 +128,20 @@ std::pair LayerInfoV2::calculateAverageFrameTime() const { for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) { // Ignore frames captured during a config change if (it->pendingConfigChange || (it + 1)->pendingConfigChange) { - continue; + return std::nullopt; } totalQueueTimeDeltas += std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod); numFrames++; - if (it->presetTime == 0 || (it + 1)->presetTime == 0) { + if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) { missingPresentTime = true; + // If there are no presentation timestamps and we haven't calculated + // one in the past then we can't calculate the refresh rate + if (mLastRefreshRate.reported == 0) { + return std::nullopt; + } continue; } @@ -142,61 +156,46 @@ std::pair LayerInfoV2::calculateAverageFrameTime() const { // when implementing render ahead for specific refresh rates. When hwui no longer provides // presentation timestamps we look at the queue time to see if the current refresh rate still // matches the content. + const auto averageFrameTime = static_cast(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) / numFrames; - return {static_cast(averageFrameTime), missingPresentTime}; -} - -bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const { - for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) { - // Ignore frames captured during a config change - if (it->pendingConfigChange || (it + 1)->pendingConfigChange) { - continue; - } - const auto presentTimeDeltas = [&] { - const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime - : (it + 1)->presetTime - it->presetTime; - return std::max(delta, mHighRefreshRatePeriod); - }(); - - if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) { - return false; - } - } - - return true; + return static_cast(averageFrameTime); } -std::optional LayerInfoV2::calculateRefreshRateIfPossible() { +std::optional LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) { static constexpr float MARGIN = 1.0f; // 1Hz if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); return std::nullopt; } - const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime(); - - // If there are no presentation timestamps provided we can't calculate the refresh rate - if (missingPresentTime && mLastRefreshRate.reported == 0) { - return std::nullopt; - } - - if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) { - return std::nullopt; - } + const auto averageFrameTime = calculateAverageFrameTime(); + if (averageFrameTime.has_value()) { + const auto refreshRate = 1e9f / *averageFrameTime; + const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now); + if (refreshRateConsistent) { + const auto knownRefreshRate = + sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate); + + // To avoid oscillation, use the last calculated refresh rate if it is + // close enough + if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN && + mLastRefreshRate.reported != knownRefreshRate) { + mLastRefreshRate.calculated = refreshRate; + mLastRefreshRate.reported = knownRefreshRate; + } - const auto refreshRate = 1e9f / averageFrameTime; - const auto knownRefreshRate = sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate); - if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN && - mLastRefreshRate.reported != knownRefreshRate) { - mLastRefreshRate.calculated = refreshRate; - mLastRefreshRate.reported = knownRefreshRate; + ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(), + refreshRate, mLastRefreshRate.reported); + } else { + ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(), + refreshRate, mLastRefreshRate.reported); + } } - ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(), refreshRate, - mLastRefreshRate.reported); - return mLastRefreshRate.reported; + return mLastRefreshRate.reported == 0 ? std::nullopt + : std::make_optional(mLastRefreshRate.reported); } std::pair LayerInfoV2::getRefreshRate(nsecs_t now) { @@ -207,15 +206,24 @@ std::pair LayerInfoV2::getRefreshRate(nsecs_ if (isAnimating(now)) { ALOGV("%s is animating", mName.c_str()); + mLastRefreshRate.animatingOrInfrequent = true; return {LayerHistory::LayerVoteType::Max, 0}; } if (!isFrequent(now)) { ALOGV("%s is infrequent", mName.c_str()); + mLastRefreshRate.animatingOrInfrequent = true; return {LayerHistory::LayerVoteType::Min, 0}; } - auto refreshRate = calculateRefreshRateIfPossible(); + // 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) { + clearHistory(now); + } + + auto refreshRate = calculateRefreshRateIfPossible(now); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; @@ -225,4 +233,65 @@ std::pair LayerInfoV2::getRefreshRate(nsecs_ return {LayerHistory::LayerVoteType::Max, 0}; } +const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const { + if (mTraceTags.count(type) == 0) { + const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type); + mTraceTags.emplace(type, tag); + } + + return mTraceTags.at(type).c_str(); +} + +LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData +LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const { + const std::string prefix = "LFPS "; + const std::string suffix = "Heuristic "; + return {.min = prefix + mName + suffix + "min", + .max = prefix + mName + suffix + "max", + .consistent = prefix + mName + suffix + "consistent", + .average = prefix + mName + suffix + "average"}; +} + +void LayerInfoV2::RefreshRateHistory::clear() { + mRefreshRates.clear(); +} + +bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) { + mRefreshRates.push_back({refreshRate, now}); + while (mRefreshRates.size() >= HISTORY_SIZE || + now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) { + mRefreshRates.pop_front(); + } + + if (CC_UNLIKELY(sTraceEnabled)) { + if (!mHeuristicTraceTagData.has_value()) { + mHeuristicTraceTagData = makeHeuristicTraceTagData(); + } + + ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast(refreshRate)); + } + + return isConsistent(); +} + +bool LayerInfoV2::RefreshRateHistory::isConsistent() const { + if (mRefreshRates.empty()) return true; + + const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end()); + const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end()); + const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS; + + if (CC_UNLIKELY(sTraceEnabled)) { + if (!mHeuristicTraceTagData.has_value()) { + mHeuristicTraceTagData = makeHeuristicTraceTagData(); + } + + ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast(max->refreshRate)); + ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast(min->refreshRate)); + ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent); + } + + return consistent; +} + } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h index c434963189..33dc66fd19 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -56,6 +56,8 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: + static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; } + static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) { sRefreshRateConfigs = &refreshRateConfigs; } @@ -90,6 +92,9 @@ public: // updated time, the updated time is the present time. nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } + // Returns a C string for tracing a vote + const char* getTraceTag(LayerHistory::LayerVoteType type) const; + void onLayerInactive(nsecs_t now) { // 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 @@ -98,6 +103,7 @@ public: const auto timePoint = std::chrono::nanoseconds(now); mFrameTimeValidSince = std::chrono::time_point(timePoint); mLastRefreshRate = {}; + mRefreshRateHistory.clear(); } void clearHistory(nsecs_t now) { @@ -113,12 +119,73 @@ private: bool pendingConfigChange; }; + // Holds information about the calculated and reported refresh rate + struct RefreshRateHeuristicData { + // Rate calculated on the layer + float calculated = 0.0f; + // Last reported rate for LayerInfoV2::getRefreshRate() + float reported = 0.0f; + // Whether the last reported rate for LayerInfoV2::getRefreshRate() + // was due to animation or infrequent updates + bool animatingOrInfrequent = false; + }; + + // Holds information about the layer vote + struct LayerVote { + LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic; + float fps = 0.0f; + }; + + // Class to store past calculated refresh rate and determine whether + // the refresh rate calculated is consistent with past values + class RefreshRateHistory { + public: + static constexpr auto HISTORY_SIZE = 90; + static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s; + + RefreshRateHistory(const std::string& name) : mName(name) {} + + // Clears History + void clear(); + + // Adds a new refresh rate and returns true if it is consistent + bool add(float refreshRate, nsecs_t now); + + private: + friend class LayerHistoryTestV2; + + // Holds the refresh rate when it was calculated + struct RefreshRateData { + float refreshRate = 0.0f; + nsecs_t timestamp = 0; + + bool operator<(const RefreshRateData& other) const { + return refreshRate < other.refreshRate; + } + }; + + // Holds tracing strings + struct HeuristicTraceTagData { + std::string min; + std::string max; + std::string consistent; + std::string average; + }; + + bool isConsistent() const; + HeuristicTraceTagData makeHeuristicTraceTagData() const; + + const std::string mName; + mutable std::optional mHeuristicTraceTagData; + std::deque mRefreshRates; + static constexpr float MARGIN_FPS = 1.0; + }; + bool isFrequent(nsecs_t now) const; bool isAnimating(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; - std::optional calculateRefreshRateIfPossible(); - std::pair calculateAverageFrameTime() const; - bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const; + std::optional calculateRefreshRateIfPossible(nsecs_t now); + std::optional calculateAverageFrameTime() const; bool isFrameTimeValid(const FrameTimeData&) const; const std::string mName; @@ -127,32 +194,27 @@ private: const nsecs_t mHighRefreshRatePeriod; LayerHistory::LayerVoteType mDefaultVote; + LayerVote mLayerVote; + nsecs_t mLastUpdatedTime = 0; nsecs_t mLastAnimationTime = 0; - // Holds information about the calculated and reported refresh rate - struct RefreshRateHeuristicData { - float calculated = 0.0f; // Rate calculated on the layer - float reported = 0.0f; // Last reported rate for LayerInfoV2::getRefreshRate() - }; - RefreshRateHeuristicData mLastRefreshRate; - // Holds information about the layer vote - struct { - LayerHistory::LayerVoteType type; - float fps; - } mLayerVote; - std::deque mFrameTimes; std::chrono::time_point mFrameTimeValidSince = std::chrono::steady_clock::now(); - static constexpr size_t HISTORY_SIZE = 90; - static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; + static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE; + static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s; + + RefreshRateHistory mRefreshRateHistory; + + mutable std::unordered_map mTraceTags; // Shared for all LayerInfo instances static const RefreshRateConfigs* sRefreshRateConfigs; + static bool sTraceEnabled; }; } // namespace scheduler diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index 2d3b01f6bd..afd2b7197f 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -38,7 +38,9 @@ protected: static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE; static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS; static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE; - static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME; + static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION; + static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION = + LayerInfoV2::RefreshRateHistory::HISTORY_DURATION; static constexpr float LO_FPS = 30.f; static constexpr auto LO_FPS_PERIOD = static_cast(1e9f / LO_FPS); @@ -84,19 +86,20 @@ protected: return sp(new mock::MockLayer(mFlinger.flinger(), std::move(name))); } - void recordFramesAndExpect(const sp& layer, float frameRate, - float desiredRefreshRate) { - nsecs_t time = systemTime(); + void recordFramesAndExpect(const sp& layer, nsecs_t& time, float frameRate, + float desiredRefreshRate, int numFrames) { const nsecs_t framePeriod = static_cast(1e9f / frameRate); - - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + impl::LayerHistoryV2::Summary summary; + for (int i = 0; i < numFrames; i++) { history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += framePeriod; + + summary = history().summarize(time); } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(desiredRefreshRate, history().summarize(time)[0].desiredRefreshRate) + ASSERT_EQ(1, summary.size()); + ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate) << "Frame rate is " << frameRate; } @@ -362,14 +365,17 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); + impl::LayerHistoryV2::Summary summary; + // layer1 is active but infrequent. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + summary = history().summarize(time); } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + ASSERT_EQ(1, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); @@ -377,28 +383,30 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; + summary = history().summarize(time); } // layer1 is still active but infrequent. history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(2, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); - ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote); + ASSERT_EQ(2, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote); + ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate); EXPECT_EQ(2, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer1 is no longer active. // layer2 is frequent and has low refresh rate. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; + summary = history().summarize(time); } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); + ASSERT_EQ(1, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); @@ -412,33 +420,36 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; + summary = history().summarize(time); } - ASSERT_EQ(2, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); - EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote); + ASSERT_EQ(2, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote); EXPECT_EQ(2, activeLayerCount()); EXPECT_EQ(2, frequentLayerCount(time)); // layer3 becomes recently active. history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); - ASSERT_EQ(2, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote); - EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate); + summary = history().summarize(time); + ASSERT_EQ(2, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); + EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate); EXPECT_EQ(2, activeLayerCount()); EXPECT_EQ(2, frequentLayerCount(time)); // layer1 expires. layer1.clear(); - ASSERT_EQ(2, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote); - EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate); + summary = history().summarize(time); + ASSERT_EQ(2, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote); + EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate); EXPECT_EQ(2, layerCount()); EXPECT_EQ(2, activeLayerCount()); EXPECT_EQ(2, frequentLayerCount(time)); @@ -448,37 +459,41 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; + summary = history().summarize(time); } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); + ASSERT_EQ(1, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer2 expires. layer2.clear(); - EXPECT_TRUE(history().summarize(time).empty()); + summary = history().summarize(time); + EXPECT_TRUE(summary.empty()); EXPECT_EQ(1, layerCount()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); // layer3 becomes active and has high refresh rate. - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) { history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; + summary = history().summarize(time); } - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); - EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate); + ASSERT_EQ(1, summary.size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate); EXPECT_EQ(1, layerCount()); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); // layer3 expires. layer3.clear(); - EXPECT_TRUE(history().summarize(time).empty()); + summary = history().summarize(time); + EXPECT_TRUE(summary.empty()); EXPECT_EQ(0, layerCount()); EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, frequentLayerCount(time)); @@ -621,21 +636,39 @@ TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) { EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + nsecs_t time = systemTime(); for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) { - recordFramesAndExpect(layer, fps, 60.0f); + recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE); } } +TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) { + const auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + + nsecs_t time = systemTime(); + recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE); + + recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE); +} + TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) { const auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); - recordFramesAndExpect(layer, 27.10f, 30.0f); - recordFramesAndExpect(layer, 26.50f, 30.0f); - recordFramesAndExpect(layer, 25.90f, 24.0f); - recordFramesAndExpect(layer, 26.50f, 24.0f); - recordFramesAndExpect(layer, 27.10f, 30.0f); + nsecs_t time = systemTime(); + + recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE); } class LayerHistoryTestV2Parameterized @@ -680,7 +713,7 @@ TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) { infrequentLayerUpdates++; } - if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) { + if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) { ASSERT_NE(0, history().summarize(time).size()); ASSERT_GE(2, history().summarize(time).size()); -- cgit v1.2.3-59-g8ed1b