diff options
11 files changed, 469 insertions, 133 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..ee612b0732 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -58,37 +58,32 @@ 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); } } // namespace -LayerHistoryV2::LayerHistoryV2() - : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {} +LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs) + : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) { + LayerInfoV2::setTraceEnabled(mTraceEnabled); + LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs); +} + LayerHistoryV2::~LayerHistoryV2() = default; void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, @@ -151,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))); } } @@ -190,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); @@ -217,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 82da00785a..44f20d0063 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -15,24 +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) { @@ -92,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; @@ -117,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; } @@ -140,58 +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}; + return static_cast<nsecs_t>(averageFrameTime); } -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; -} - -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 && mLastReportedRefreshRate == 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; - if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) { - mLastReportedRefreshRate = refreshRate; + 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("Refresh rate: %.2f", mLastReportedRefreshRate); - return mLastReportedRefreshRate; + return mLastRefreshRate.reported == 0 ? std::nullopt + : std::make_optional(mLastRefreshRate.reported); } std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { @@ -202,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()}; @@ -220,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 9e6783f854..33dc66fd19 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -56,6 +56,12 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: + static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; } + + static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) { + sRefreshRateConfigs = &refreshRateConfigs; + } + LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); @@ -86,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 @@ -93,7 +102,8 @@ public: // posting infrequent updates. const auto timePoint = std::chrono::nanoseconds(now); mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint); - mLastReportedRefreshRate = 0.0f; + mLastRefreshRate = {}; + mRefreshRateHistory.clear(); } void clearHistory(nsecs_t now) { @@ -109,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; @@ -123,23 +194,27 @@ private: const nsecs_t mHighRefreshRatePeriod; LayerHistory::LayerVoteType mDefaultVote; + LayerVote mLayerVote; + nsecs_t mLastUpdatedTime = 0; nsecs_t mLastAnimationTime = 0; - float mLastReportedRefreshRate = 0.0f; - - // Holds information about the layer vote - struct { - LayerHistory::LayerVoteType type; - float fps; - } mLayerVote; + RefreshRateHeuristicData mLastRefreshRate; 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/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<std::shared_ptr<const HWC2::Display::Config>>& 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<float> RefreshRateConfigs::constructKnownFrameRates( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { + std::vector<float> 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<std::shared_ptr<const HWC2::Display::Config>>& configs, HwcConfigIndexType currentConfigId); private: + friend class RefreshRateConfigsTest; + void constructAvailableRefreshRates() REQUIRES(mLock); + static std::vector<float> constructKnownFrameRates( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs); void getSortedRefreshRateList( const std::function<bool(const RefreshRate&)>& 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<float> 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<scheduler::impl::LayerHistoryV2>(); + mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig); } else { mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>(); } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index f376b4a3c1..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,6 +86,23 @@ protected: return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name))); } + 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); + 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, summary.size()); + ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); + ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate) + << "Frame rate is " << frameRate; + } + Hwc2::mock::Display mDisplay; RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0) .setVsyncPeriod(int32_t(LO_FPS_PERIOD)) @@ -346,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)); @@ -361,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)); @@ -396,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)); @@ -432,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)); @@ -600,6 +631,46 @@ 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())); + + nsecs_t time = systemTime(); + for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) { + 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())); + + 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 : public LayerHistoryTestV2, public testing::WithParamInterface<std::chrono::nanoseconds> {}; @@ -642,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()); 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<float> 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<RefreshRateConfigs>(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<RefreshRateConfigs>(m60_90Device, + /*currentConfigId=*/HWC_CONFIG_ID_60); + + struct ExpectedRate { + float rate; + const RefreshRate& expected; + }; + + /* clang-format off */ + std::vector<ExpectedRate> 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>{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<scheduler::impl::LayerHistoryV2>(); + mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs); } else { mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>(); } @@ -43,7 +43,7 @@ public: : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this, useContentDetectionV2, true) { if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(); + mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs); } else { mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>(); } |