diff options
Diffstat (limited to 'services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp')
-rw-r--r-- | services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp | 870 |
1 files changed, 270 insertions, 600 deletions
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 0334d70bd5..8661b6ee0a 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -17,58 +17,20 @@ // #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra" - #include "RefreshRateConfigs.h" #include <android-base/stringprintf.h> #include <utils/Trace.h> #include <chrono> #include <cmath> -#include "../SurfaceFlingerProperties.h" #undef LOG_TAG #define LOG_TAG "RefreshRateConfigs" namespace android::scheduler { -namespace { -std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) { - return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(), - RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight, - toString(layer.seamlessness).c_str(), - to_string(layer.desiredRefreshRate).c_str()); -} - -std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) { - std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)}; - knownFrameRates.reserve(knownFrameRates.size() + modes.size()); - - // Add all supported refresh rates to the set - for (const auto& mode : modes) { - const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod()); - knownFrameRates.emplace_back(refreshRate); - } - - // Sort and remove duplicates - std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess); - knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(), - Fps::EqualsWithMargin()), - knownFrameRates.end()); - return knownFrameRates; -} - -} // namespace using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; using RefreshRate = RefreshRateConfigs::RefreshRate; -std::string RefreshRate::toString() const { - return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}", - getModeId().value(), mode->getHwcId(), getFps().getValue(), - mode->getWidth(), mode->getHeight(), getModeGroup()); -} - std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { switch (vote) { case LayerVoteType::NoVote: @@ -83,184 +45,80 @@ std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { return "ExplicitDefault"; case LayerVoteType::ExplicitExactOrMultiple: return "ExplicitExactOrMultiple"; - case LayerVoteType::ExplicitExact: - return "ExplicitExact"; - } -} - -std::string RefreshRateConfigs::Policy::toString() const { - return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d" - ", primary range: %s, app request range: %s", - defaultMode.value(), allowGroupSwitching, - primaryRange.toString().c_str(), appRequestRange.toString().c_str()); -} - -std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod, - nsecs_t displayPeriod) const { - auto [quotient, remainder] = std::div(layerPeriod, displayPeriod); - if (remainder <= MARGIN_FOR_PERIOD_CALCULATION || - std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { - quotient++; - remainder = 0; } - - return {quotient, remainder}; } -bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, - const RefreshRate& refreshRate) const { - switch (layer.vote) { - case LayerVoteType::ExplicitExactOrMultiple: - case LayerVoteType::Heuristic: - if (mConfig.frameRateMultipleThreshold != 0 && - refreshRate.fps.greaterThanOrEqualWithMargin( - Fps(mConfig.frameRateMultipleThreshold)) && - layer.desiredRefreshRate.lessThanWithMargin( - Fps(mConfig.frameRateMultipleThreshold / 2))) { - // Don't vote high refresh rates past the threshold for layers with a low desired - // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for - // 120 Hz, but desired 60 fps should have a vote. - return false; +const RefreshRate& RefreshRateConfigs::getRefreshRateForContent( + const std::vector<LayerRequirement>& layers) const { + std::lock_guard lock(mLock); + int contentFramerate = 0; + int explicitContentFramerate = 0; + for (const auto& layer : layers) { + const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate); + if (layer.vote == LayerVoteType::ExplicitDefault || + layer.vote == LayerVoteType::ExplicitExactOrMultiple) { + if (desiredRefreshRateRound > explicitContentFramerate) { + explicitContentFramerate = desiredRefreshRateRound; + } + } else { + if (desiredRefreshRateRound > contentFramerate) { + contentFramerate = desiredRefreshRateRound; } - break; - case LayerVoteType::ExplicitDefault: - case LayerVoteType::ExplicitExact: - case LayerVoteType::Max: - case LayerVoteType::Min: - case LayerVoteType::NoVote: - break; - } - return true; -} - -float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, - const RefreshRate& refreshRate, - bool isSeamlessSwitch) const { - if (!isVoteAllowed(layer, refreshRate)) { - return 0; - } - - // Slightly prefer seamless switches. - constexpr float kSeamedSwitchPenalty = 0.95f; - const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; - - // If the layer wants Max, give higher score to the higher refresh rate - if (layer.vote == LayerVoteType::Max) { - const auto ratio = - refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue(); - // use ratio^2 to get a lower score the more we get further from peak - return ratio * ratio; - } - - const auto displayPeriod = refreshRate.getVsyncPeriod(); - const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs(); - if (layer.vote == LayerVoteType::ExplicitDefault) { - // Find the actual rate the layer will render, assuming - // that layerPeriod is the minimal time to render a frame - auto actualLayerPeriod = displayPeriod; - int multiplier = 1; - while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) { - multiplier++; - actualLayerPeriod = displayPeriod * multiplier; - } - return std::min(1.0f, - static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod)); - } - - if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || - layer.vote == LayerVoteType::Heuristic) { - // Calculate how many display vsyncs we need to present a single frame for this - // layer - const auto [displayFramesQuotient, displayFramesRemainder] = - getDisplayFrames(layerPeriod, displayPeriod); - static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 - if (displayFramesRemainder == 0) { - // Layer desired refresh rate matches the display rate. - return 1.0f * seamlessness; - } - - if (displayFramesQuotient == 0) { - // Layer desired refresh rate is higher than the display rate. - return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) * - (1.0f / (MAX_FRAMES_TO_FIT + 1)); - } - - // Layer desired refresh rate is lower than the display rate. Check how well it fits - // the cadence. - auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder)); - int iter = 2; - while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) { - diff = diff - (displayPeriod - diff); - iter++; } - - return (1.0f / iter) * seamlessness; } - if (layer.vote == LayerVoteType::ExplicitExact) { - const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate); - if (mSupportsFrameRateOverride) { - // Since we support frame rate override, allow refresh rates which are - // multiples of the layer's request, as those apps would be throttled - // down to run at the desired refresh rate. - return divider > 0; - } - - return divider == 1; + if (explicitContentFramerate != 0) { + contentFramerate = explicitContentFramerate; + } else if (contentFramerate == 0) { + contentFramerate = round<int>(mMaxSupportedRefreshRate->getFps()); } + ATRACE_INT("ContentFPS", contentFramerate); - return 0; -} - -struct RefreshRateScore { - const RefreshRate* refreshRate; - float score; -}; - -RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers, - const GlobalSignals& globalSignals, - GlobalSignals* outSignalsConsidered) const { - std::lock_guard lock(mLock); + // Find the appropriate refresh rate with minimal error + auto iter = min_element(mPrimaryRefreshRates.cbegin(), mPrimaryRefreshRates.cend(), + [contentFramerate](const auto& lhs, const auto& rhs) -> bool { + return std::abs(lhs->fps - contentFramerate) < + std::abs(rhs->fps - contentFramerate); + }); - if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) { - return *cached; + // Some content aligns better on higher refresh rate. For example for 45fps we should choose + // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't + // align well with both + const RefreshRate* bestSoFar = *iter; + constexpr float MARGIN = 0.05f; + float ratio = (*iter)->fps / contentFramerate; + if (std::abs(std::round(ratio) - ratio) > MARGIN) { + while (iter != mPrimaryRefreshRates.cend()) { + ratio = (*iter)->fps / contentFramerate; + + if (std::abs(std::round(ratio) - ratio) <= MARGIN) { + bestSoFar = *iter; + break; + } + ++iter; + } } - GlobalSignals signalsConsidered; - RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered); - lastBestRefreshRateInvocation.emplace( - GetBestRefreshRateInvocation{.layerRequirements = layers, - .globalSignals = globalSignals, - .outSignalsConsidered = signalsConsidered, - .resultingBestRefreshRate = result}); - if (outSignalsConsidered) { - *outSignalsConsidered = signalsConsidered; - } - return result; + return *bestSoFar; } -std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate( - const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals, - GlobalSignals* outSignalsConsidered) const { - const bool sameAsLastCall = lastBestRefreshRateInvocation && - lastBestRefreshRateInvocation->layerRequirements == layers && - lastBestRefreshRateInvocation->globalSignals == globalSignals; - - if (sameAsLastCall) { - if (outSignalsConsidered) { - *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered; - } - return lastBestRefreshRateInvocation->resultingBestRefreshRate; +std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod, + nsecs_t displayPeriod) const { + auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod); + if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION || + std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { + displayFramesQuot++; + displayFramesRem = 0; } - return {}; + return {displayFramesQuot, displayFramesRem}; } -RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( +const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals, GlobalSignals* outSignalsConsidered) const { ATRACE_CALL(); - ALOGV("getBestRefreshRate %zu layers", layers.size()); + ALOGV("getRefreshRateForContent %zu layers", layers.size()); if (outSignalsConsidered) *outSignalsConsidered = {}; const auto setTouchConsidered = [&] { @@ -275,48 +133,32 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( } }; + std::lock_guard lock(mLock); + int noVoteLayers = 0; int minVoteLayers = 0; int maxVoteLayers = 0; int explicitDefaultVoteLayers = 0; int explicitExactOrMultipleVoteLayers = 0; - int explicitExact = 0; float maxExplicitWeight = 0; - int seamedFocusedLayers = 0; for (const auto& layer : layers) { - switch (layer.vote) { - case LayerVoteType::NoVote: - noVoteLayers++; - break; - case LayerVoteType::Min: - minVoteLayers++; - break; - case LayerVoteType::Max: - maxVoteLayers++; - break; - case LayerVoteType::ExplicitDefault: - explicitDefaultVoteLayers++; - maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); - break; - case LayerVoteType::ExplicitExactOrMultiple: - explicitExactOrMultipleVoteLayers++; - maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); - break; - case LayerVoteType::ExplicitExact: - explicitExact++; - maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); - break; - case LayerVoteType::Heuristic: - break; - } - - if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) { - seamedFocusedLayers++; + if (layer.vote == LayerVoteType::NoVote) { + noVoteLayers++; + } else if (layer.vote == LayerVoteType::Min) { + minVoteLayers++; + } else if (layer.vote == LayerVoteType::Max) { + maxVoteLayers++; + } else if (layer.vote == LayerVoteType::ExplicitDefault) { + explicitDefaultVoteLayers++; + maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); + } else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) { + explicitExactOrMultipleVoteLayers++; + maxExplicitWeight = std::max(maxExplicitWeight, layer.weight); } } - const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 || - explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0; + const bool hasExplicitVoteLayers = + explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0; // 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. @@ -330,8 +172,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // move out the of range if layers explicitly request a different refresh // rate. const Policy* policy = getCurrentPolicyLocked(); - const bool primaryRangeIsSingleRate = - policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max); + const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max; if (!globalSignals.touch && globalSignals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { @@ -351,19 +192,16 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( } // Find the best refresh rate based on score - std::vector<RefreshRateScore> scores; + std::vector<std::pair<const RefreshRate*, float>> scores; scores.reserve(mAppRequestRefreshRates.size()); for (const auto refreshRate : mAppRequestRefreshRates) { - scores.emplace_back(RefreshRateScore{refreshRate, 0.0f}); + scores.emplace_back(refreshRate, 0.0f); } - const auto& defaultMode = mRefreshRates.at(policy->defaultMode); - for (const auto& layer : layers) { - ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(), - layerVoteTypeString(layer.vote).c_str(), layer.weight, - layer.desiredRefreshRate.getValue()); + ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(), + layerVoteTypeString(layer.vote).c_str(), layer.weight); if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { continue; } @@ -371,59 +209,88 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( auto weight = layer.weight; for (auto i = 0u; i < scores.size(); i++) { - const bool isSeamlessSwitch = - scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup(); - - if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) { - ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s", - formatLayerInfo(layer, weight).c_str(), - scores[i].refreshRate->toString().c_str(), - mCurrentRefreshRate->toString().c_str()); + bool inPrimaryRange = + scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max); + if ((primaryRangeIsSingleRate || !inPrimaryRange) && + !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) { + // Only focused layers with ExplicitDefault frame rate settings are allowed to score + // refresh rates outside the primary range. continue; } - if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch && - !layer.focused) { - ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed." - " Current mode = %s", - formatLayerInfo(layer, weight).c_str(), - scores[i].refreshRate->toString().c_str(), - mCurrentRefreshRate->toString().c_str()); + // If the layer wants Max, give higher score to the higher refresh rate + if (layer.vote == LayerVoteType::Max) { + const auto ratio = scores[i].first->fps / scores.back().first->fps; + // use ratio^2 to get a lower score the more we get further from peak + const auto layerScore = ratio * ratio; + ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight, + scores[i].first->name.c_str(), layerScore); + scores[i].second += weight * layerScore; continue; } - // Layers with default seamlessness vote for the current mode group if - // there are layers with seamlessness=SeamedAndSeamless and for the default - // mode group otherwise. In second case, if the current mode group is different - // from the default, this means a layer with seamlessness=SeamedAndSeamless has just - // disappeared. - const bool isInPolicyForDefault = seamedFocusedLayers > 0 - ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup() - : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup(); - - if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) { - ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(), - scores[i].refreshRate->toString().c_str(), - mCurrentRefreshRate->toString().c_str()); + const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod(); + const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate); + if (layer.vote == LayerVoteType::ExplicitDefault) { + const auto layerScore = [&]() { + // Find the actual rate the layer will render, assuming + // that layerPeriod is the minimal time to render a frame + auto actualLayerPeriod = displayPeriod; + int multiplier = 1; + while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) { + multiplier++; + actualLayerPeriod = displayPeriod * multiplier; + } + return std::min(1.0f, + static_cast<float>(layerPeriod) / + static_cast<float>(actualLayerPeriod)); + }(); + + ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f", + layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), + layerScore); + scores[i].second += weight * layerScore; continue; } - bool inPrimaryRange = scores[i].refreshRate->inPolicy(policy->primaryRange.min, - policy->primaryRange.max); - if ((primaryRangeIsSingleRate || !inPrimaryRange) && - !(layer.focused && - (layer.vote == LayerVoteType::ExplicitDefault || - layer.vote == LayerVoteType::ExplicitExact))) { - // Only focused layers with ExplicitDefault frame rate settings are allowed to score - // refresh rates outside the primary range. + if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || + layer.vote == LayerVoteType::Heuristic) { + const auto layerScore = [&] { + // Calculate how many display vsyncs we need to present a single frame for this + // layer + const auto [displayFramesQuot, displayFramesRem] = + getDisplayFrames(layerPeriod, displayPeriod); + static constexpr size_t MAX_FRAMES_TO_FIT = + 10; // Stop calculating when score < 0.1 + if (displayFramesRem == 0) { + // Layer desired refresh rate matches the display rate. + return 1.0f; + } + + if (displayFramesQuot == 0) { + // Layer desired refresh rate is higher the display rate. + return (static_cast<float>(layerPeriod) / + static_cast<float>(displayPeriod)) * + (1.0f / (MAX_FRAMES_TO_FIT + 1)); + } + + // Layer desired refresh rate is lower the display rate. Check how well it fits + // the cadence + auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem)); + int iter = 2; + while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) { + diff = diff - (displayPeriod - diff); + iter++; + } + + return 1.0f / iter; + }(); + ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), + layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod, + scores[i].first->name.c_str(), layerScore); + scores[i].second += weight * layerScore; continue; } - - const auto layerScore = - calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch); - ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(), - scores[i].refreshRate->getName().c_str(), layerScore); - scores[i].score += weight * layerScore; } } @@ -438,7 +305,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // If we never scored any layers, then choose the rate from the primary // range instead of picking a random score from the app range. if (std::all_of(scores.begin(), scores.end(), - [](RefreshRateScore score) { return score.score == 0; })) { + [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) { ALOGV("layers not scored - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); return getMaxRefreshRateByPolicyLocked(); @@ -453,17 +320,8 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // actually increase the refresh rate over the normal selection. const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); - const bool touchBoostForExplicitExact = [&] { - if (mSupportsFrameRateOverride) { - // Enable touch boost if there are other layers besides exact - return explicitExact + noVoteLayers != layers.size(); - } else { - // Enable touch boost if there are no exact layers - return explicitExact == 0; - } - }(); - if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && - bestRefreshRate->fps.lessThanWithMargin(touchRefreshRate.fps)) { + if (globalSignals.touch && explicitDefaultVoteLayers == 0 && + bestRefreshRate->fps < touchRefreshRate.fps) { setTouchConsidered(); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; @@ -472,124 +330,16 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( return *bestRefreshRate; } -std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> -groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) { - std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid; - for (const auto& layer : layers) { - auto iter = layersByUid.emplace(layer.ownerUid, - std::vector<const RefreshRateConfigs::LayerRequirement*>()); - auto& layersWithSameUid = iter.first->second; - layersWithSameUid.push_back(&layer); - } - - // Remove uids that can't have a frame rate override - for (auto iter = layersByUid.begin(); iter != layersByUid.end();) { - const auto& layersWithSameUid = iter->second; - bool skipUid = false; - for (const auto& layer : layersWithSameUid) { - if (layer->vote == RefreshRateConfigs::LayerVoteType::Max || - layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) { - skipUid = true; - break; - } - } - if (skipUid) { - iter = layersByUid.erase(iter); - } else { - ++iter; - } - } - - return layersByUid; -} - -std::vector<RefreshRateScore> initializeScoresForAllRefreshRates( - const AllRefreshRatesMapType& refreshRates) { - std::vector<RefreshRateScore> scores; - scores.reserve(refreshRates.size()); - for (const auto& [ignored, refreshRate] : refreshRates) { - scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f}); - } - std::sort(scores.begin(), scores.end(), - [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; }); - return scores; -} - -RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides( - const std::vector<LayerRequirement>& layers, Fps displayFrameRate, bool touch) const { - ATRACE_CALL(); - if (!mSupportsFrameRateOverride) return {}; - - ALOGV("getFrameRateOverrides %zu layers", layers.size()); - std::lock_guard lock(mLock); - std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates); - std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid = - groupLayersByUid(layers); - UidToFrameRateOverride frameRateOverrides; - for (const auto& [uid, layersWithSameUid] : layersByUid) { - // Layers with ExplicitExactOrMultiple expect touch boost - const bool hasExplicitExactOrMultiple = - std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(), - [](const auto& layer) { - return layer->vote == LayerVoteType::ExplicitExactOrMultiple; - }); - - if (touch && hasExplicitExactOrMultiple) { - continue; - } - - for (auto& score : scores) { - score.score = 0; - } - - for (const auto& layer : layersWithSameUid) { - if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) { - continue; - } - - LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault && - layer->vote != LayerVoteType::ExplicitExactOrMultiple && - layer->vote != LayerVoteType::ExplicitExact); - for (RefreshRateScore& score : scores) { - const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate, - /*isSeamlessSwitch*/ true); - score.score += layer->weight * layerScore; - } - } - - // We just care about the refresh rates which are a divider of the - // display refresh rate - auto iter = - std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) { - return getFrameRateDivider(displayFrameRate, score.refreshRate->getFps()) == 0; - }); - scores.erase(iter, scores.end()); - - // If we never scored any layers, we don't have a preferred frame rate - if (std::all_of(scores.begin(), scores.end(), - [](const RefreshRateScore& score) { return score.score == 0; })) { - continue; - } - - // Now that we scored all the refresh rates we need to pick the one that got the highest - // score. - const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end()); - frameRateOverrides.emplace(uid, bestRefreshRate->getFps()); - } - - return frameRateOverrides; -} - template <typename Iter> const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const { constexpr auto EPSILON = 0.001f; - const RefreshRate* bestRefreshRate = begin->refreshRate; - float max = begin->score; + const RefreshRate* bestRefreshRate = begin->first; + float max = begin->second; for (auto i = begin; i != end; ++i) { const auto [refreshRate, score] = *i; - ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score); + ALOGV("%s scores %.2f", refreshRate->name.c_str(), score); - ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100)); + ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100)); if (score > max * (1 + EPSILON)) { max = score; @@ -600,60 +350,34 @@ const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) return bestRefreshRate; } -std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged( - std::optional<DisplayModeId> desiredActiveConfigId, bool timerExpired) const { - std::lock_guard lock(mLock); - - const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId) - : *mCurrentRefreshRate; - const auto& min = *mMinSupportedRefreshRate; - - if (current != min) { - const auto& refreshRate = timerExpired ? min : current; - return refreshRate.getFps(); - } +const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const { + return mRefreshRates; +} - return {}; +const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const { + std::lock_guard lock(mLock); + return getMinRefreshRateByPolicyLocked(); } const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const { - for (auto refreshRate : mPrimaryRefreshRates) { - if (mCurrentRefreshRate->getModeGroup() == refreshRate->getModeGroup()) { - return *refreshRate; - } - } - ALOGE("Can't find min refresh rate by policy with the same mode group" - " as the current mode %s", - mCurrentRefreshRate->toString().c_str()); - // Defaulting to the lowest refresh rate return *mPrimaryRefreshRates.front(); } -RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const { +const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const { std::lock_guard lock(mLock); return getMaxRefreshRateByPolicyLocked(); } const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const { - for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) { - const auto& refreshRate = (**it); - if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) { - return refreshRate; - } - } - ALOGE("Can't find max refresh rate by policy with the same mode group" - " as the current mode %s", - mCurrentRefreshRate->toString().c_str()); - // Defaulting to the highest refresh rate return *mPrimaryRefreshRates.back(); } -RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const { +const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const { std::lock_guard lock(mLock); return *mCurrentRefreshRate; } -RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const { +const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const { std::lock_guard lock(mLock); return getCurrentRefreshRateByPolicyLocked(); } @@ -663,95 +387,60 @@ const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() con mCurrentRefreshRate) != mAppRequestRefreshRates.end()) { return *mCurrentRefreshRate; } - return *mRefreshRates.at(getCurrentPolicyLocked()->defaultMode); -} - -void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) { - std::lock_guard lock(mLock); - - // Invalidate the cached invocation to getBestRefreshRate. This forces - // the refresh rate to be recomputed on the next call to getBestRefreshRate. - lastBestRefreshRateInvocation.reset(); - - mCurrentRefreshRate = mRefreshRates.at(modeId).get(); -} - -RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId, - Config config) - : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) { - updateDisplayModes(modes, currentModeId); + return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig); } -void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes, - DisplayModeId currentModeId) { +void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) { std::lock_guard lock(mLock); - - // The current mode should be supported - LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) { - return mode->getId() == currentModeId; - })); - - // Invalidate the cached invocation to getBestRefreshRate. This forces - // the refresh rate to be recomputed on the next call to getBestRefreshRate. - lastBestRefreshRateInvocation.reset(); - - mRefreshRates.clear(); - for (const auto& mode : modes) { - const auto modeId = mode->getId(); - mRefreshRates.emplace(modeId, - std::make_unique<RefreshRate>(modeId, mode, mode->getFps(), + mCurrentRefreshRate = mRefreshRates.at(configId).get(); +} + +RefreshRateConfigs::RefreshRateConfigs( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, + HwcConfigIndexType currentConfigId) + : mKnownFrameRates(constructKnownFrameRates(configs)) { + LOG_ALWAYS_FATAL_IF(configs.empty()); + LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size()); + + for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) { + const auto& config = configs.at(static_cast<size_t>(configId.value())); + const float fps = 1e9f / config->getVsyncPeriod(); + mRefreshRates.emplace(configId, + std::make_unique<RefreshRate>(configId, config, + base::StringPrintf("%.0ffps", fps), fps, RefreshRate::ConstructorTag(0))); - if (modeId == currentModeId) { - mCurrentRefreshRate = mRefreshRates.at(modeId).get(); - } - } - - std::vector<const RefreshRate*> sortedModes; - getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedModes); - // Reset the policy because the old one may no longer be valid. - mDisplayManagerPolicy = {}; - mDisplayManagerPolicy.defaultMode = currentModeId; - mMinSupportedRefreshRate = sortedModes.front(); - mMaxSupportedRefreshRate = sortedModes.back(); - - mSupportsFrameRateOverride = false; - if (mConfig.enableFrameRateOverride) { - for (const auto& mode1 : sortedModes) { - for (const auto& mode2 : sortedModes) { - if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) { - mSupportsFrameRateOverride = true; - break; - } - } + if (configId == currentConfigId) { + mCurrentRefreshRate = mRefreshRates.at(configId).get(); } } + std::vector<const RefreshRate*> sortedConfigs; + getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs); + mDisplayManagerPolicy.defaultConfig = currentConfigId; + mMinSupportedRefreshRate = sortedConfigs.front(); + mMaxSupportedRefreshRate = sortedConfigs.back(); constructAvailableRefreshRates(); } -bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const { - // defaultMode must be a valid mode, and within the given refresh rate range. - auto iter = mRefreshRates.find(policy.defaultMode); +bool RefreshRateConfigs::isPolicyValid(const Policy& policy) { + // defaultConfig must be a valid config, and within the given refresh rate range. + auto iter = mRefreshRates.find(policy.defaultConfig); if (iter == mRefreshRates.end()) { - ALOGE("Default mode is not found."); return false; } const RefreshRate& refreshRate = *iter->second; if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) { - ALOGE("Default mode is not in the primary range."); return false; } - return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) && - policy.appRequestRange.max.greaterThanOrEqualWithMargin(policy.primaryRange.max); + return policy.appRequestRange.min <= policy.primaryRange.min && + policy.appRequestRange.max >= policy.primaryRange.max; } status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { std::lock_guard lock(mLock); - if (!isPolicyValidLocked(policy)) { - ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str()); + if (!isPolicyValid(policy)) { return BAD_VALUE; } - lastBestRefreshRateInvocation.reset(); Policy previousPolicy = *getCurrentPolicyLocked(); mDisplayManagerPolicy = policy; if (*getCurrentPolicyLocked() == previousPolicy) { @@ -763,10 +452,9 @@ status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) { std::lock_guard lock(mLock); - if (policy && !isPolicyValidLocked(*policy)) { + if (policy && !isPolicyValid(*policy)) { return BAD_VALUE; } - lastBestRefreshRateInvocation.reset(); Policy previousPolicy = *getCurrentPolicyLocked(); mOverridePolicy = policy; if (*getCurrentPolicyLocked() == previousPolicy) { @@ -790,76 +478,79 @@ RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const { return mDisplayManagerPolicy; } -bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const { +bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const { std::lock_guard lock(mLock); for (const RefreshRate* refreshRate : mAppRequestRefreshRates) { - if (refreshRate->modeId == modeId) { + if (refreshRate->configId == config) { return true; } } return false; } -void RefreshRateConfigs::getSortedRefreshRateListLocked( +void RefreshRateConfigs::getSortedRefreshRateList( const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate, std::vector<const RefreshRate*>* outRefreshRates) { outRefreshRates->clear(); outRefreshRates->reserve(mRefreshRates.size()); for (const auto& [type, refreshRate] : mRefreshRates) { if (shouldAddRefreshRate(*refreshRate)) { - ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy", - refreshRate->modeId.value()); + ALOGV("getSortedRefreshRateList: config %d added to list policy", + refreshRate->configId.value()); outRefreshRates->push_back(refreshRate.get()); } } std::sort(outRefreshRates->begin(), outRefreshRates->end(), [](const auto refreshRate1, const auto refreshRate2) { - if (refreshRate1->mode->getVsyncPeriod() != - refreshRate2->mode->getVsyncPeriod()) { - return refreshRate1->mode->getVsyncPeriod() > - refreshRate2->mode->getVsyncPeriod(); + if (refreshRate1->hwcConfig->getVsyncPeriod() != + refreshRate2->hwcConfig->getVsyncPeriod()) { + return refreshRate1->hwcConfig->getVsyncPeriod() > + refreshRate2->hwcConfig->getVsyncPeriod(); } else { - return refreshRate1->mode->getGroup() > refreshRate2->mode->getGroup(); + return refreshRate1->hwcConfig->getConfigGroup() > + refreshRate2->hwcConfig->getConfigGroup(); } }); } void RefreshRateConfigs::constructAvailableRefreshRates() { - // Filter modes based on current policy and sort based on vsync period + // Filter configs based on current policy and sort based on vsync period const Policy* policy = getCurrentPolicyLocked(); - const auto& defaultMode = mRefreshRates.at(policy->defaultMode)->mode; - ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str()); - - auto filterRefreshRates = - [&](Fps min, Fps max, const char* listName, - std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock) { - getSortedRefreshRateListLocked( - [&](const RefreshRate& refreshRate) REQUIRES(mLock) { - const auto& mode = refreshRate.mode; - - return mode->getHeight() == defaultMode->getHeight() && - mode->getWidth() == defaultMode->getWidth() && - mode->getDpiX() == defaultMode->getDpiX() && - mode->getDpiY() == defaultMode->getDpiY() && - (policy->allowGroupSwitching || - mode->getGroup() == defaultMode->getGroup()) && - refreshRate.inPolicy(min, max); - }, - outRefreshRates); - - LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(), - "No matching modes for %s range: min=%s max=%s", listName, - to_string(min).c_str(), to_string(max).c_str()); - auto stringifyRefreshRates = [&]() -> std::string { - std::string str; - for (auto refreshRate : *outRefreshRates) { - base::StringAppendF(&str, "%s ", refreshRate->getName().c_str()); - } - return str; - }; - ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str()); - }; + const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig)->hwcConfig; + ALOGV("constructAvailableRefreshRates: default %d group %d primaryRange=[%.2f %.2f]" + " appRequestRange=[%.2f %.2f]", + policy->defaultConfig.value(), defaultConfig->getConfigGroup(), policy->primaryRange.min, + policy->primaryRange.max, policy->appRequestRange.min, policy->appRequestRange.max); + + auto filterRefreshRates = [&](float min, float max, const char* listName, + std::vector<const RefreshRate*>* outRefreshRates) { + getSortedRefreshRateList( + [&](const RefreshRate& refreshRate) REQUIRES(mLock) { + const auto& hwcConfig = refreshRate.hwcConfig; + + return hwcConfig->getHeight() == defaultConfig->getHeight() && + hwcConfig->getWidth() == defaultConfig->getWidth() && + hwcConfig->getDpiX() == defaultConfig->getDpiX() && + hwcConfig->getDpiY() == defaultConfig->getDpiY() && + (policy->allowGroupSwitching || + hwcConfig->getConfigGroup() == defaultConfig->getConfigGroup()) && + refreshRate.inPolicy(min, max); + }, + outRefreshRates); + + LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(), + "No matching configs for %s range: min=%.0f max=%.0f", listName, min, + max); + auto stringifyRefreshRates = [&]() -> std::string { + std::string str; + for (auto refreshRate : *outRefreshRates) { + base::StringAppendF(&str, "%s ", refreshRate->name.c_str()); + } + return str; + }; + ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str()); + }; filterRefreshRates(policy->primaryRange.min, policy->primaryRange.max, "primary", &mPrimaryRefreshRates); @@ -867,29 +558,47 @@ void RefreshRateConfigs::constructAvailableRefreshRates() { &mAppRequestRefreshRates); } -Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const { - if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) { +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.greaterThanOrEqualWithMargin(*std::prev(mKnownFrameRates.end()))) { + if (frameRate >= *std::prev(mKnownFrameRates.end())) { return *std::prev(mKnownFrameRates.end()); } - auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate, - Fps::comparesLess); + auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate); - const auto distance1 = std::abs((frameRate.getValue() - lowerBound->getValue())); - const auto distance2 = std::abs((frameRate.getValue() - std::prev(lowerBound)->getValue())); + const auto distance1 = std::abs(frameRate - *lowerBound); + const auto distance2 = std::abs(frameRate - *std::prev(lowerBound)); return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound); } RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const { std::lock_guard lock(mLock); - const auto& deviceMin = *mMinSupportedRefreshRate; + const auto& deviceMin = getMinRefreshRate(); const auto& minByPolicy = getMinRefreshRateByPolicyLocked(); const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked(); - const auto& currentPolicy = getCurrentPolicyLocked(); // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that // the min allowed refresh rate is higher than the device min, we do not want to enable the @@ -898,9 +607,10 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction return RefreshRateConfigs::KernelIdleTimerAction::TurnOff; } if (minByPolicy == maxByPolicy) { - // when min primary range in display manager policy is below device min turn on the timer. - if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) { - return RefreshRateConfigs::KernelIdleTimerAction::TurnOn; + // Do not sent the call to toggle off kernel idle timer if the device min and policy min and + // max are all the same. This saves us extra unnecessary calls to sysprop. + if (deviceMin == minByPolicy) { + return RefreshRateConfigs::KernelIdleTimerAction::NoChange; } return RefreshRateConfigs::KernelIdleTimerAction::TurnOff; } @@ -908,44 +618,4 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction return RefreshRateConfigs::KernelIdleTimerAction::TurnOn; } -int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) { - // This calculation needs to be in sync with the java code - // in DisplayManagerService.getDisplayInfoForFrameRateOverride - constexpr float kThreshold = 0.1f; - const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue(); - const auto numPeriodsRounded = std::round(numPeriods); - if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) { - return 0; - } - - return static_cast<int>(numPeriodsRounded); -} - -void RefreshRateConfigs::dump(std::string& result) const { - std::lock_guard lock(mLock); - base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n", - mDisplayManagerPolicy.toString().c_str()); - scheduler::RefreshRateConfigs::Policy currentPolicy = *getCurrentPolicyLocked(); - if (mOverridePolicy && currentPolicy != mDisplayManagerPolicy) { - base::StringAppendF(&result, "DesiredDisplayModeSpecs (Override): %s\n\n", - currentPolicy.toString().c_str()); - } - - auto mode = mCurrentRefreshRate->mode; - base::StringAppendF(&result, "Current mode: %s\n", mCurrentRefreshRate->toString().c_str()); - - result.append("Refresh rates:\n"); - for (const auto& [id, refreshRate] : mRefreshRates) { - mode = refreshRate->mode; - base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str()); - } - - base::StringAppendF(&result, "Supports Frame Rate Override: %s\n", - mSupportsFrameRateOverride ? "yes" : "no"); - result.append("\n"); -} - } // namespace android::scheduler - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra" |