SurfaceFlinger: override frame rate for applications that use setFrameRate
When possible, override the frame rate of an app if it explicitly
set a preferred frame rate.
Test: atest FrameRateOverrideHostTest
Test: SF unit tests
Bug: 170502573
Change-Id: Ifcb7dd1ad0423b05efe8a0e86f2d988c49e74054
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 4b7251b..cd03c18 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -71,16 +71,84 @@
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;
+ 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 {displayFramesQuot, displayFramesRem};
+ return {quotient, remainder};
}
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+ const RefreshRate& refreshRate,
+ bool isSeamlessSwitch) const {
+ // 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;
+ }
+
+ return 0;
+}
+
+struct RefreshRateScore {
+ const RefreshRate* refreshRate;
+ float score;
+};
+
const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
GlobalSignals* outSignalsConsidered) const {
@@ -165,11 +233,11 @@
}
// Find the best refresh rate based on score
- std::vector<std::pair<const RefreshRate*, float>> scores;
+ std::vector<RefreshRateScore> scores;
scores.reserve(mAppRequestRefreshRates.size());
for (const auto refreshRate : mAppRequestRefreshRates) {
- scores.emplace_back(refreshRate, 0.0f);
+ scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
}
const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
@@ -184,12 +252,13 @@
auto weight = layer.weight;
for (auto i = 0u; i < scores.size(); i++) {
- const bool isSeamlessSwitch =
- scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup();
+ const bool isSeamlessSwitch = scores[i].refreshRate->getConfigGroup() ==
+ mCurrentRefreshRate->getConfigGroup();
if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
ALOGV("%s ignores %s to avoid non-seamless switch. Current config = %s",
- formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+ formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
mCurrentRefreshRate->toString().c_str());
continue;
}
@@ -198,7 +267,8 @@
!layer.focused) {
ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
" Current config = %s",
- formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+ formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
mCurrentRefreshRate->toString().c_str());
continue;
}
@@ -209,18 +279,20 @@
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
const bool isInPolicyForDefault = seamedLayers > 0
- ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
- : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+ ? scores[i].refreshRate->getConfigGroup() ==
+ mCurrentRefreshRate->getConfigGroup()
+ : scores[i].refreshRate->getConfigGroup() == defaultConfig->getConfigGroup();
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
!layer.focused) {
ALOGV("%s ignores %s. Current config = %s", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->toString().c_str(), mCurrentRefreshRate->toString().c_str());
+ scores[i].refreshRate->toString().c_str(),
+ mCurrentRefreshRate->toString().c_str());
continue;
}
- bool inPrimaryRange =
- scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
+ bool inPrimaryRange = scores[i].refreshRate->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
@@ -228,81 +300,11 @@
continue;
}
- // 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.getValue() / scores.back().first->fps.getValue();
- // use ratio^2 to get a lower score the more we get further from peak
- const auto layerScore = ratio * ratio;
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->getName().c_str(), layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
-
- const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
- const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
- 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 gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->getName().c_str(), layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
-
- 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;
- }();
- // Slightly prefer seamless switches.
- constexpr float kSeamedSwitchPenalty = 0.95f;
- const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->getName().c_str(), layerScore);
- scores[i].second += weight * layerScore * seamlessness;
- 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;
}
}
@@ -317,7 +319,7 @@
// 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(),
- [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+ [](RefreshRateScore score) { return score.score == 0; })) {
ALOGV("layers not scored - choose %s",
getMaxRefreshRateByPolicyLocked().getName().c_str());
return getMaxRefreshRateByPolicyLocked();
@@ -342,11 +344,110 @@
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) const {
+ ATRACE_CALL();
+ 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) {
+ 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);
+ 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());
+
+ // If the nest refresh rate is the current one, we don't have an override
+ if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) {
+ 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->first;
- float max = begin->second;
+ const RefreshRate* bestRefreshRate = begin->refreshRate;
+ float max = begin->score;
for (auto i = begin; i != end; ++i) {
const auto [refreshRate, score] = *i;
ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
@@ -649,50 +750,22 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
-void RefreshRateConfigs::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
- if (frameRateOverride.frameRateHz > 0 && frameRateOverride.frameRateHz < 1) {
- return;
- }
-
- std::lock_guard lock(mLock);
- if (frameRateOverride.frameRateHz != 0) {
- mPreferredRefreshRateForUid[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
- } else {
- mPreferredRefreshRateForUid.erase(frameRateOverride.uid);
- }
-}
-
-int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
- std::lock_guard lock(mLock);
-
- const auto iter = mPreferredRefreshRateForUid.find(uid);
- if (iter == mPreferredRefreshRateForUid.end()) {
- return 1;
- }
-
+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 refreshRateHz = iter->second;
- const auto numPeriods = mCurrentRefreshRate->getFps().getValue() / refreshRateHz.getValue();
+ const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
const auto numPeriodsRounded = std::round(numPeriods);
if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
- return 1;
+ return 0;
}
return static_cast<int>(numPeriodsRounded);
}
-std::vector<FrameRateOverride> RefreshRateConfigs::getFrameRateOverrides() {
+int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const {
std::lock_guard lock(mLock);
- std::vector<FrameRateOverride> overrides;
- overrides.reserve(mPreferredRefreshRateForUid.size());
-
- for (const auto [uid, frameRate] : mPreferredRefreshRateForUid) {
- overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
- }
-
- return overrides;
+ return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate);
}
void RefreshRateConfigs::dump(std::string& result) const {