diff options
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerHistoryV2.cpp | 35 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfo.h | 6 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfoV2.cpp | 169 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfoV2.h | 96 | ||||
| -rw-r--r-- | services/surfaceflinger/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<Layer>& weak, LayerHistory::LayerVoteType type, int fps) { +void trace(const wp<Layer>& 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<Layer>& 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<int>(std::round(refreshRate))); + trace(layer, *info, type, static_cast<int>(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<nsecs_t> 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 <algorithm> #include <utility> +#include <cutils/compiler.h> +#include <cutils/trace.h> + #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<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const { +std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const { nsecs_t totalPresentTimeDeltas = 0; nsecs_t totalQueueTimeDeltas = 0; bool missingPresentTime = false; @@ -119,15 +128,20 @@ std::pair<nsecs_t, bool> 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<nsecs_t, bool> 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<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) / numFrames; - return {static_cast<nsecs_t>(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<nsecs_t>(averageFrameTime); } -std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() { +std::optional<float> 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<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { @@ -207,15 +206,24 @@ std::pair<LayerHistory::LayerVoteType, float> 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<LayerHistory::LayerVoteType, float> 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<int>(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<int>(max->refreshRate)); + ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(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<std::chrono::steady_clock>(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<HeuristicTraceTagData> mHeuristicTraceTagData; + std::deque<RefreshRateData> 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<float> calculateRefreshRateIfPossible(); - std::pair<nsecs_t, bool> calculateAverageFrameTime() const; - bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const; + std::optional<float> calculateRefreshRateIfPossible(nsecs_t now); + std::optional<nsecs_t> 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<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; + static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE; + static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s; + + RefreshRateHistory mRefreshRateHistory; + + mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> 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<nsecs_t>(1e9f / LO_FPS); @@ -84,19 +86,20 @@ protected: return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name))); } - void recordFramesAndExpect(const sp<mock::MockLayer>& layer, float frameRate, - float desiredRefreshRate) { - nsecs_t time = systemTime(); + void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate, + float desiredRefreshRate, int numFrames) { const nsecs_t framePeriod = static_cast<nsecs_t>(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()); |