Add smooth switch bool for setFrameRateCategory
This allows the platform to have a different behavior depending on
whether a device is MRR or dVRR. When the bool is `true`, MRR devices
(those with DisplayModes that do not have vrr config) will not change
frame rates if it would cause jank. The expected usage is to mark the
bool true when an animation is running.
Bug: 300491171
Test: atest libsurfaceflinger_unittest
Change-Id: I5e87d276c11ecc806ede3e943f0a6498a7b910c4
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 4e5659e..069d89b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -194,7 +194,8 @@
to_string(vote.fps).c_str(), categoryString.c_str(),
weight * 100);
summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
- vote.seamlessness, vote.category, weight, layerFocused});
+ vote.seamlessness, vote.category, vote.categorySmoothSwitchOnly,
+ weight, layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
trace(*info, vote.type, vote.fps.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index dd96930..36f2475 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -309,7 +309,8 @@
ALOGV("%s uses frame rate category: %d", mName.c_str(),
static_cast<int>(mLayerVote.category));
votes.push_back({LayerHistory::LayerVoteType::ExplicitCategory, Fps(),
- Seamlessness::Default, mLayerVote.category});
+ Seamlessness::Default, mLayerVote.category,
+ mLayerVote.categorySmoothSwitchOnly});
}
if (mLayerVote.fps.isValid() ||
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index d580b58..7d3cffa 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -71,8 +71,8 @@
LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
Fps fps;
Seamlessness seamlessness = Seamlessness::Default;
- // Category is in effect if fps is not specified.
FrameRateCategory category = FrameRateCategory::Default;
+ bool categorySmoothSwitchOnly = false;
// Returns true if the layer explicitly should contribute to frame rate scoring.
bool isNoVote() const { return RefreshRateSelector::isNoVote(type); }
@@ -111,6 +111,7 @@
} vote;
FrameRateCategory category = FrameRateCategory::Default;
+ bool categorySmoothSwitchOnly = false;
FrameRate() = default;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index b06723d..1d23fb5 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -494,6 +494,7 @@
int explicitExact = 0;
int explicitCategoryVoteLayers = 0;
int seamedFocusedLayers = 0;
+ int categorySmoothSwitchOnlyLayers = 0;
for (const auto& layer : layers) {
switch (layer.vote) {
@@ -531,6 +532,9 @@
if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
seamedFocusedLayers++;
}
+ if (layer.frameRateCategorySmoothSwitchOnly) {
+ categorySmoothSwitchOnlyLayers++;
+ }
}
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
@@ -578,10 +582,17 @@
return {ranking, kNoSignals};
}
+ const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0;
+ const DisplayModeId activeModeId = activeMode.getId();
+
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
ALOGV("All layers Min");
- const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
+ const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending,
+ std::nullopt, [&](FrameRateMode mode) {
+ return !smoothSwitchOnly ||
+ mode.modePtr->getId() == activeModeId;
+ });
ATRACE_FORMAT_INSTANT("%s (All layers Min)",
to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
@@ -627,6 +638,14 @@
continue;
}
+ if (smoothSwitchOnly && modePtr->getId() != activeModeId) {
+ ALOGV("%s ignores %s because it's non-VRR and smooth switch only."
+ " Current mode = %s",
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+ to_string(activeMode).c_str());
+ 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
@@ -770,6 +789,7 @@
to_string(descending.front().frameRateMode.fps).c_str());
return {descending, kNoSignals};
} else {
+ ALOGV("primaryRangeIsSingleRate");
ATRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
@@ -805,6 +825,7 @@
// If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
// current config
if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+ ALOGV("preferredDisplayMode");
const auto ascendingWithPreferred =
rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
ATRACE_FORMAT_INSTANT("%s (preferredDisplayMode)",
@@ -812,6 +833,7 @@
return {ascendingWithPreferred, kNoSignals};
}
+ ALOGV("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
ATRACE_FORMAT_INSTANT("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
@@ -1017,7 +1039,8 @@
auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt) const
+ std::optional<DisplayModeId> preferredDisplayModeOpt,
+ const RankFrameRatesPredicate& predicate) const
-> FrameRateRanking {
using fps_approx_ops::operator<;
const char* const whence = __func__;
@@ -1044,7 +1067,8 @@
std::deque<ScoredFrameRate> ranking;
const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
const auto& modePtr = frameRateMode.modePtr;
- if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
+ if ((anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) ||
+ !predicate(frameRateMode)) {
return;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 5d32414..545b939 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -170,8 +170,11 @@
Fps desiredRefreshRate;
// If a seamless mode switch is required.
Seamlessness seamlessness = Seamlessness::Default;
- // Layer frame rate category. Superseded by desiredRefreshRate.
+ // Layer frame rate category.
FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+ // Goes together with frame rate category vote. Allow refresh rate changes only
+ // if there would be no jank.
+ bool frameRateCategorySmoothSwitchOnly = false;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
// would have on choosing the refresh rate.
float weight = 0.0f;
@@ -446,10 +449,15 @@
ftl_last = Descending
};
- // Only uses the primary range, not the app request range.
+ typedef std::function<bool(const FrameRateMode)> RankFrameRatesPredicate;
+
+ // Rank the frame rates.
+ // Only modes in the primary range for which `predicate` is `true` will be scored.
+ // Does not use the app requested range.
FrameRateRanking rankFrameRates(
std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
+ std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt,
+ const RankFrameRatesPredicate& predicate = [](FrameRateMode) { return true; }) const
REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);