Loading services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ LayerHistoryV2::~LayerHistoryV2() = default; void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate); auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type); auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type); std::lock_guard lock(mLock); mLayerInfos.emplace_back(layer, std::move(info)); } Loading services/surfaceflinger/Scheduler/LayerInfoV2.cpp +19 −36 Original line number Diff line number Diff line Loading @@ -27,8 +27,10 @@ namespace android::scheduler { LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) : mHighRefreshRatePeriod(highRefreshRatePeriod), LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) : mName(name), mHighRefreshRatePeriod(highRefreshRatePeriod), mDefaultVote(defaultVote), mLayerVote({defaultVote, 0.0f}) {} Loading @@ -45,54 +47,31 @@ void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) { } } bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>( mFrameTimeValidSince.time_since_epoch()) .count(); } bool LayerInfoV2::isFrequent(nsecs_t now) const { // Find the first valid frame time auto it = mFrameTimes.begin(); for (; it != mFrameTimes.end(); ++it) { if (isFrameTimeValid(*it)) { break; } for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) { if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) { ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(), (now - mFrameTimes.back().queueTime) / 1e6f); return false; } // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) { const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1); if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) { ALOGV("%s frequent (burst of %zu frames", mName.c_str(), numFrames); return true; } // Find the first active frame for (; it != mFrameTimes.end(); ++it) { if (it->queueTime >= getActiveLayerThreshold(now)) { break; } } const auto numFrames = std::distance(it, mFrameTimes.end()); if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) { ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size()); return false; } // Layer is considered frequent if the average frame rate is higher than the threshold const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER; } bool LayerInfoV2::hasEnoughDataForHeuristic() const { // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates if (mFrameTimes.size() < 2) { return false; } if (!isFrameTimeValid(mFrameTimes.front())) { return false; } if (mFrameTimes.size() < HISTORY_SIZE && mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) { return false; Loading Loading @@ -167,18 +146,22 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() { std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); return {mLayerVote.type, mLayerVote.fps}; } if (!isFrequent(now)) { ALOGV("%s is infrequent", mName.c_str()); return {LayerHistory::LayerVoteType::Min, 0}; } auto refreshRate = calculateRefreshRateIfPossible(); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; } ALOGV("%s Max (can't resolve refresh rate", mName.c_str()); return {LayerHistory::LayerVoteType::Max, 0}; } Loading services/surfaceflinger/Scheduler/LayerInfoV2.h +5 −9 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); LayerInfoV2(const LayerInfo&) = delete; LayerInfoV2& operator=(const LayerInfoV2&) = delete; Loading Loading @@ -83,11 +84,7 @@ public: nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } void clearHistory() { // Mark mFrameTimeValidSince to now to ignore all previous frame times. // We are not deleting the old frame to keep track of whether we should treat the first // buffer as Max as we don't know anything about this layer or Min as this layer is // posting infrequent updates. mFrameTimeValidSince = std::chrono::steady_clock::now(); mFrameTimes.clear(); mLastReportedRefreshRate = 0.0f; } Loading @@ -101,7 +98,8 @@ private: bool isFrequent(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; std::optional<float> calculateRefreshRateIfPossible(); bool isFrameTimeValid(const FrameTimeData&) const; const std::string mName; // Used for sanitizing the heuristic data const nsecs_t mHighRefreshRatePeriod; Loading @@ -118,8 +116,6 @@ private: } mLayerVote; std::deque<FrameTimeData> mFrameTimes; std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince = std::chrono::steady_clock::now(); static constexpr size_t HISTORY_SIZE = 90; static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; }; Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +11 −6 Original line number Diff line number Diff line Loading @@ -103,7 +103,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); *touchConsidered = false; if (touchConsidered) *touchConsidered = false; std::lock_guard lock(mLock); int noVoteLayers = 0; Loading Loading @@ -131,7 +131,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) { *touchConsidered = true; ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); if (touchConsidered) *touchConsidered = true; return getMaxRefreshRateByPolicyLocked(); } Loading @@ -145,6 +146,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Only if all layers want Min we should return Min if (noVoteLayers + minVoteLayers == layers.size()) { ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); return getMinRefreshRateByPolicyLocked(); } Loading Loading @@ -243,9 +245,11 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( return 1.0f / iter; }(); ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), layer.vote == LayerVoteType::ExplicitExactOrMultiple ? "ExplicitExactOrMultiple" : "Heuristic", weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } Loading @@ -266,7 +270,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); if (touchActive && explicitDefaultVoteLayers == 0 && bestRefreshRate->fps < touchRefreshRate.fps) { *touchConsidered = true; if (touchConsidered) *touchConsidered = true; ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; } Loading services/surfaceflinger/Scheduler/Scheduler.cpp +14 −18 Original line number Diff line number Diff line Loading @@ -526,7 +526,9 @@ void Scheduler::idleTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) { const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */); if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) { mLayerHistory->clear(); } ATRACE_INT("TouchState", static_cast<int>(touch)); } Loading @@ -549,18 +551,19 @@ void Scheduler::dump(std::string& result) const { } template <class T> void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { ConfigEvent event = ConfigEvent::None; HwcConfigIndexType newConfigId; bool touchConsidered = false; { std::lock_guard<std::mutex> lock(mFeatureStateLock); if (*currentState == newState) { return; return touchConsidered; } *currentState = newState; newConfigId = calculateRefreshRateConfigIndexType(); newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered); if (mFeatures.configId == newConfigId) { return; return touchConsidered; } mFeatures.configId = newConfigId; if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) { Loading @@ -569,10 +572,12 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO } const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, event); return touchConsidered; } HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) { ATRACE_CALL(); if (touchConsidered) *touchConsidered = false; // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. Loading Loading @@ -607,18 +612,9 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { .getConfigId(); } bool touchConsidered; const auto& ret = mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, &touchConsidered) return mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered) .getConfigId(); if (touchConsidered) { // Clear layer history if refresh rate was selected based on touch to allow // the hueristic to pick up with the new rate. mLayerHistory->clear(); } return ret; } std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() { Loading Loading
services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ LayerHistoryV2::~LayerHistoryV2() = default; void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate); auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type); auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type); std::lock_guard lock(mLock); mLayerInfos.emplace_back(layer, std::move(info)); } Loading
services/surfaceflinger/Scheduler/LayerInfoV2.cpp +19 −36 Original line number Diff line number Diff line Loading @@ -27,8 +27,10 @@ namespace android::scheduler { LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) : mHighRefreshRatePeriod(highRefreshRatePeriod), LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) : mName(name), mHighRefreshRatePeriod(highRefreshRatePeriod), mDefaultVote(defaultVote), mLayerVote({defaultVote, 0.0f}) {} Loading @@ -45,54 +47,31 @@ void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) { } } bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>( mFrameTimeValidSince.time_since_epoch()) .count(); } bool LayerInfoV2::isFrequent(nsecs_t now) const { // Find the first valid frame time auto it = mFrameTimes.begin(); for (; it != mFrameTimes.end(); ++it) { if (isFrameTimeValid(*it)) { break; } for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) { if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) { ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(), (now - mFrameTimes.back().queueTime) / 1e6f); return false; } // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) { const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1); if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) { ALOGV("%s frequent (burst of %zu frames", mName.c_str(), numFrames); return true; } // Find the first active frame for (; it != mFrameTimes.end(); ++it) { if (it->queueTime >= getActiveLayerThreshold(now)) { break; } } const auto numFrames = std::distance(it, mFrameTimes.end()); if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) { ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size()); return false; } // Layer is considered frequent if the average frame rate is higher than the threshold const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER; } bool LayerInfoV2::hasEnoughDataForHeuristic() const { // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates if (mFrameTimes.size() < 2) { return false; } if (!isFrameTimeValid(mFrameTimes.front())) { return false; } if (mFrameTimes.size() < HISTORY_SIZE && mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) { return false; Loading Loading @@ -167,18 +146,22 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() { std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); return {mLayerVote.type, mLayerVote.fps}; } if (!isFrequent(now)) { ALOGV("%s is infrequent", mName.c_str()); return {LayerHistory::LayerVoteType::Min, 0}; } auto refreshRate = calculateRefreshRateIfPossible(); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; } ALOGV("%s Max (can't resolve refresh rate", mName.c_str()); return {LayerHistory::LayerVoteType::Max, 0}; } Loading
services/surfaceflinger/Scheduler/LayerInfoV2.h +5 −9 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ class LayerInfoV2 { friend class LayerHistoryTestV2; public: LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); LayerInfoV2(const LayerInfo&) = delete; LayerInfoV2& operator=(const LayerInfoV2&) = delete; Loading Loading @@ -83,11 +84,7 @@ public: nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } void clearHistory() { // Mark mFrameTimeValidSince to now to ignore all previous frame times. // We are not deleting the old frame to keep track of whether we should treat the first // buffer as Max as we don't know anything about this layer or Min as this layer is // posting infrequent updates. mFrameTimeValidSince = std::chrono::steady_clock::now(); mFrameTimes.clear(); mLastReportedRefreshRate = 0.0f; } Loading @@ -101,7 +98,8 @@ private: bool isFrequent(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; std::optional<float> calculateRefreshRateIfPossible(); bool isFrameTimeValid(const FrameTimeData&) const; const std::string mName; // Used for sanitizing the heuristic data const nsecs_t mHighRefreshRatePeriod; Loading @@ -118,8 +116,6 @@ private: } mLayerVote; std::deque<FrameTimeData> mFrameTimes; std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince = std::chrono::steady_clock::now(); static constexpr size_t HISTORY_SIZE = 90; static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; }; Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +11 −6 Original line number Diff line number Diff line Loading @@ -103,7 +103,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); *touchConsidered = false; if (touchConsidered) *touchConsidered = false; std::lock_guard lock(mLock); int noVoteLayers = 0; Loading Loading @@ -131,7 +131,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) { *touchConsidered = true; ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); if (touchConsidered) *touchConsidered = true; return getMaxRefreshRateByPolicyLocked(); } Loading @@ -145,6 +146,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Only if all layers want Min we should return Min if (noVoteLayers + minVoteLayers == layers.size()) { ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); return getMinRefreshRateByPolicyLocked(); } Loading Loading @@ -243,9 +245,11 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( return 1.0f / iter; }(); ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), layer.vote == LayerVoteType::ExplicitExactOrMultiple ? "ExplicitExactOrMultiple" : "Heuristic", weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } Loading @@ -266,7 +270,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); if (touchActive && explicitDefaultVoteLayers == 0 && bestRefreshRate->fps < touchRefreshRate.fps) { *touchConsidered = true; if (touchConsidered) *touchConsidered = true; ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; } Loading
services/surfaceflinger/Scheduler/Scheduler.cpp +14 −18 Original line number Diff line number Diff line Loading @@ -526,7 +526,9 @@ void Scheduler::idleTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) { const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */); if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) { mLayerHistory->clear(); } ATRACE_INT("TouchState", static_cast<int>(touch)); } Loading @@ -549,18 +551,19 @@ void Scheduler::dump(std::string& result) const { } template <class T> void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { ConfigEvent event = ConfigEvent::None; HwcConfigIndexType newConfigId; bool touchConsidered = false; { std::lock_guard<std::mutex> lock(mFeatureStateLock); if (*currentState == newState) { return; return touchConsidered; } *currentState = newState; newConfigId = calculateRefreshRateConfigIndexType(); newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered); if (mFeatures.configId == newConfigId) { return; return touchConsidered; } mFeatures.configId = newConfigId; if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) { Loading @@ -569,10 +572,12 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO } const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, event); return touchConsidered; } HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) { ATRACE_CALL(); if (touchConsidered) *touchConsidered = false; // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. Loading Loading @@ -607,18 +612,9 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { .getConfigId(); } bool touchConsidered; const auto& ret = mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, &touchConsidered) return mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered) .getConfigId(); if (touchConsidered) { // Clear layer history if refresh rate was selected based on touch to allow // the hueristic to pick up with the new rate. mLayerHistory->clear(); } return ret; } std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() { Loading