Add setFrameRateCategory surface API
Bug: 284911776
Test: atest CtsSurfaceControlTestsStaging
Test: atest libsurfaceflinger_unittest
Change-Id: Ia804a63198ff096d1e5ffedf6046a0350963b8ed
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 5d00a26..565a490 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -40,8 +40,9 @@
namespace {
bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
- // Layers with an explicit vote are always kept active
- if (info.getSetFrameRateVote().rate.isValid()) {
+ // Layers with an explicit frame rate or frame rate category are always kept active,
+ // but ignore NoVote/NoPreference.
+ if (info.getSetFrameRateVote().isValid() && !info.getSetFrameRateVote().isNoVote()) {
return true;
}
@@ -70,6 +71,7 @@
traceType(LayerHistory::LayerVoteType::ExplicitExact, fps);
traceType(LayerHistory::LayerVoteType::Min, 1);
traceType(LayerHistory::LayerVoteType::Max, 1);
+ traceType(LayerHistory::LayerVoteType::ExplicitCategory, 1);
ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
@@ -171,27 +173,32 @@
layerFocused ? "" : "not");
ATRACE_FORMAT("%s", info->getName().c_str());
- const auto vote = info->getRefreshRateVote(selector, now);
- // Skip NoVote layer as those don't have any requirements
- if (vote.type == LayerVoteType::NoVote) {
- continue;
- }
+ const auto votes = info->getRefreshRateVote(selector, now);
+ for (LayerInfo::LayerVote vote : votes) {
+ if (vote.isNoVote()) {
+ continue;
+ }
- // Compute the layer's position on the screen
- const Rect bounds = Rect(info->getBounds());
- const ui::Transform transform = info->getTransform();
- constexpr bool roundOutwards = true;
- Rect transformed = transform.transform(bounds, roundOutwards);
+ // Compute the layer's position on the screen
+ const Rect bounds = Rect(info->getBounds());
+ const ui::Transform transform = info->getTransform();
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
- const float layerArea = transformed.getWidth() * transformed.getHeight();
- float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- ATRACE_FORMAT_INSTANT("%s %s (%d%)", ftl::enum_string(vote.type).c_str(),
- to_string(vote.fps).c_str(), weight * 100);
- summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
- vote.seamlessness, weight, layerFocused});
+ const float layerArea = transformed.getWidth() * transformed.getHeight();
+ float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+ const std::string categoryString = vote.category == FrameRateCategory::Default
+ ? base::StringPrintf("category=%s", ftl::enum_string(vote.category).c_str())
+ : "";
+ ATRACE_FORMAT_INSTANT("%s %s %s (%d%)", ftl::enum_string(vote.type).c_str(),
+ 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});
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(*info, vote.type, vote.fps.getIntValue());
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, vote.type, vote.fps.getIntValue());
+ }
}
}
@@ -228,7 +235,7 @@
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
const auto voteType = [&]() {
- switch (frameRate.type) {
+ switch (frameRate.vote.type) {
case Layer::FrameRateCompatibility::Default:
return LayerVoteType::ExplicitDefault;
case Layer::FrameRateCompatibility::Min:
@@ -242,9 +249,10 @@
}
}();
- if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) {
+ if (frameRate.isValid()) {
const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
+ info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
+ frameRate.category});
} else {
info->resetLayerVote();
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index bae3739..750803b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -26,10 +26,12 @@
#include <algorithm>
#include <utility>
+#include <android/native_window.h>
#include <cutils/compiler.h>
#include <cutils/trace.h>
#include <ftl/enum.h>
#include <gui/TraceUtils.h>
+#include <system/window.h>
#undef LOG_TAG
#define LOG_TAG "LayerInfo"
@@ -265,19 +267,34 @@
: std::nullopt;
}
-LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
- nsecs_t now) {
+LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
+ nsecs_t now) {
ATRACE_CALL();
+ LayerInfo::RefreshRateVotes votes;
+
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
- ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
- return mLayerVote;
+ if (mLayerVote.category != FrameRateCategory::Default) {
+ ALOGV("%s uses frame rate category: %d", mName.c_str(),
+ static_cast<int>(mLayerVote.category));
+ votes.push_back({LayerHistory::LayerVoteType::ExplicitCategory, mLayerVote.fps,
+ Seamlessness::Default, mLayerVote.category});
+ }
+
+ if (mLayerVote.fps.isValid() ||
+ mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) {
+ ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+ votes.push_back(mLayerVote);
+ }
+
+ return votes;
}
if (isAnimating(now)) {
ATRACE_FORMAT_INSTANT("animating");
ALOGV("%s is animating", mName.c_str());
mLastRefreshRate.animating = true;
- return {LayerHistory::LayerVoteType::Max, Fps()};
+ votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
+ return votes;
}
const LayerInfo::Frequent frequent = isFrequent(now);
@@ -288,7 +305,8 @@
mLastRefreshRate.infrequent = true;
// Infrequent layers vote for minimal refresh rate for
// battery saving purposes and also to prevent b/135718869.
- return {LayerHistory::LayerVoteType::Min, Fps()};
+ votes.push_back({LayerHistory::LayerVoteType::Min, Fps()});
+ return votes;
}
if (frequent.clearHistory) {
@@ -298,11 +316,13 @@
auto refreshRate = calculateRefreshRateIfPossible(selector, now);
if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
- return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+ votes.push_back({LayerHistory::LayerVoteType::Heuristic, refreshRate.value()});
+ return votes;
}
ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
- return {LayerHistory::LayerVoteType::Max, Fps()};
+ votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
+ return votes;
}
const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
@@ -391,6 +411,68 @@
return consistent;
}
+LayerInfo::FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
+ switch (compatibility) {
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
+ return FrameRateCompatibility::Default;
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
+ return FrameRateCompatibility::ExactOrMultiple;
+ case ANATIVEWINDOW_FRAME_RATE_EXACT:
+ return FrameRateCompatibility::Exact;
+ case ANATIVEWINDOW_FRAME_RATE_MIN:
+ return FrameRateCompatibility::Min;
+ case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
+ return FrameRateCompatibility::NoVote;
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
+ return FrameRateCompatibility::Default;
+ }
+}
+
+Seamlessness LayerInfo::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+ switch (strategy) {
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+ return Seamlessness::OnlySeamless;
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+ return Seamlessness::SeamedAndSeamless;
+ default:
+ LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+ return Seamlessness::Default;
+ }
+}
+
+FrameRateCategory LayerInfo::FrameRate::convertCategory(int8_t category) {
+ switch (category) {
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT:
+ return FrameRateCategory::Default;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE:
+ return FrameRateCategory::NoPreference;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_LOW:
+ return FrameRateCategory::Low;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL:
+ return FrameRateCategory::Normal;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH:
+ return FrameRateCategory::High;
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate category value %d", category);
+ return FrameRateCategory::Default;
+ }
+}
+
+bool LayerInfo::FrameRate::isNoVote() const {
+ return vote.type == FrameRateCompatibility::NoVote ||
+ category == FrameRateCategory::NoPreference;
+}
+
+bool LayerInfo::FrameRate::isValid() const {
+ return isNoVote() || vote.rate.isValid() || category != FrameRateCategory::Default;
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayerInfo::FrameRate& rate) {
+ return stream << "{rate=" << rate.vote.rate << " type=" << ftl::enum_string(rate.vote.type)
+ << " seamlessness=" << ftl::enum_string(rate.vote.seamlessness) << '}';
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index c5a6057..7d2444c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -25,6 +25,7 @@
#include <ui/Transform.h>
#include <utils/Timers.h>
+#include <scheduler/Fps.h>
#include <scheduler/Seamlessness.h>
#include "LayerHistory.h"
@@ -67,8 +68,15 @@
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;
+
+ // Returns true if the layer explicitly should contribute to frame rate scoring.
+ bool isNoVote() const { return RefreshRateSelector::isNoVote(type, category); }
};
+ using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>;
+
// FrameRateCompatibility specifies how we should interpret the frame rate associated with
// the layer.
enum class FrameRateCompatibility {
@@ -87,24 +95,40 @@
ftl_last = NoVote
};
- // Encapsulates the frame rate and compatibility of the layer. This information will be used
+ // Encapsulates the frame rate specifications of the layer. This information will be used
// when the display refresh rate is determined.
struct FrameRate {
using Seamlessness = scheduler::Seamlessness;
- Fps rate;
- FrameRateCompatibility type = FrameRateCompatibility::Default;
- Seamlessness seamlessness = Seamlessness::Default;
+ // Information related to a specific desired frame rate vote.
+ struct FrameRateVote {
+ Fps rate;
+ FrameRateCompatibility type = FrameRateCompatibility::Default;
+ Seamlessness seamlessness = Seamlessness::Default;
+
+ bool operator==(const FrameRateVote& other) const {
+ return isApproxEqual(rate, other.rate) && type == other.type &&
+ seamlessness == other.seamlessness;
+ }
+
+ FrameRateVote() = default;
+
+ FrameRateVote(Fps rate, FrameRateCompatibility type,
+ Seamlessness seamlessness = Seamlessness::OnlySeamless)
+ : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+ } vote;
+
+ FrameRateCategory category = FrameRateCategory::Default;
FrameRate() = default;
FrameRate(Fps rate, FrameRateCompatibility type,
- Seamlessness seamlessness = Seamlessness::OnlySeamless)
- : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+ Seamlessness seamlessness = Seamlessness::OnlySeamless,
+ FrameRateCategory category = FrameRateCategory::Default)
+ : vote(FrameRateVote(rate, type, seamlessness)), category(category) {}
bool operator==(const FrameRate& other) const {
- return isApproxEqual(rate, other.rate) && type == other.type &&
- seamlessness == other.seamlessness;
+ return vote == other.vote && category == other.category;
}
bool operator!=(const FrameRate& other) const { return !(*this == other); }
@@ -112,8 +136,22 @@
// Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
// Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+
+ // Convert an ANATIVEWINDOW_CHANGE_FRAME_RATE_* value to a scheduler::Seamlessness.
+ // Logs fatal if the compatibility value is invalid.
static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
+ // Convert an ANATIVEWINDOW_FRAME_RATE_CATEGORY_* value to a FrameRateCategory.
+ // Logs fatal if the compatibility value is invalid.
+ static FrameRateCategory convertCategory(int8_t category);
+
+ // True if the FrameRate has explicit frame rate specifications.
+ bool isValid() const;
+
+ // Returns true if the FrameRate explicitly instructs to not contribute to frame rate
+ // selection.
+ bool isNoVote() const;
+
private:
static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
if (!rate.isValid()) {
@@ -148,13 +186,15 @@
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
// Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default}; }
+ void resetLayerVote() {
+ mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default, FrameRateCategory::Default};
+ }
std::string getName() const { return mName; }
uid_t getOwnerUid() const { return mOwnerUid; }
- LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
+ RefreshRateVotes getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index fb985f7..a7ba731 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -275,6 +275,25 @@
return {quotient, remainder};
}
+float RefreshRateSelector::calculateNonExactMatchingDefaultLayerScoreLocked(
+ nsecs_t displayPeriod, nsecs_t layerPeriod) const {
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal period to render a frame.
+ // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+ // then the actualLayerPeriod will be 32ms, because it is the
+ // smallest multiple of the display period which is >= layerPeriod.
+ auto actualLayerPeriod = displayPeriod;
+ int multiplier = 1;
+ while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+ multiplier++;
+ actualLayerPeriod = displayPeriod * multiplier;
+ }
+
+ // Because of the threshold we used above it's possible that score is slightly
+ // above 1.
+ return std::min(1.0f, static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+}
+
float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
Fps refreshRate) const {
constexpr float kScoreForFractionalPairs = .8f;
@@ -282,22 +301,7 @@
const auto displayPeriod = refreshRate.getPeriodNsecs();
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 period to render a frame.
- // For example if layerPeriod is 20ms and displayPeriod is 16ms,
- // then the actualLayerPeriod will be 32ms, because it is the
- // smallest multiple of the display period which is >= layerPeriod.
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
-
- // Because of the threshold we used above it's possible that score is slightly
- // above 1.
- return std::min(1.0f,
- static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+ return calculateNonExactMatchingDefaultLayerScoreLocked(displayPeriod, layerPeriod);
}
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
@@ -372,6 +376,22 @@
constexpr float kSeamedSwitchPenalty = 0.95f;
const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+ if (layer.vote == LayerVoteType::ExplicitCategory) {
+ if (getFrameRateCategoryRange(layer.frameRateCategory).includes(refreshRate)) {
+ return 1.f;
+ }
+
+ FpsRange categoryRange = getFrameRateCategoryRange(layer.frameRateCategory);
+ using fps_approx_ops::operator<;
+ if (refreshRate < categoryRange.min) {
+ return calculateNonExactMatchingDefaultLayerScoreLocked(refreshRate.getPeriodNsecs(),
+ categoryRange.min
+ .getPeriodNsecs());
+ }
+ return calculateNonExactMatchingDefaultLayerScoreLocked(refreshRate.getPeriodNsecs(),
+ categoryRange.max.getPeriodNsecs());
+ }
+
// If the layer wants Max, give higher score to the higher refresh rate
if (layer.vote == LayerVoteType::Max) {
return calculateDistanceScoreFromMax(refreshRate);
@@ -391,7 +411,8 @@
// If the layer frame rate is a divisor of the refresh rate it should score
// the highest score.
- if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
+ if (layer.desiredRefreshRate.isValid() &&
+ getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
return 1.0f * seamlessness;
}
@@ -441,6 +462,7 @@
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
int explicitExact = 0;
+ int explicitCategoryVoteLayers = 0;
int seamedFocusedLayers = 0;
for (const auto& layer : layers) {
@@ -463,6 +485,9 @@
case LayerVoteType::ExplicitExact:
explicitExact++;
break;
+ case LayerVoteType::ExplicitCategory:
+ explicitCategoryVoteLayers++;
+ break;
case LayerVoteType::Heuristic:
break;
}
@@ -473,7 +498,8 @@
}
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
- explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+ explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0 ||
+ explicitCategoryVoteLayers > 0;
const Policy* policy = getCurrentPolicyLocked();
const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
@@ -536,10 +562,11 @@
}
for (const auto& layer : layers) {
- ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
- ftl::enum_string(layer.vote).c_str(), layer.weight,
- layer.desiredRefreshRate.getValue());
- if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
+ ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f, category %s) ",
+ layer.name.c_str(), ftl::enum_string(layer.vote).c_str(), layer.weight,
+ layer.desiredRefreshRate.getValue(),
+ ftl::enum_string(layer.frameRateCategory).c_str());
+ if (layer.isNoVote() || layer.vote == LayerVoteType::Min) {
continue;
}
@@ -607,6 +634,7 @@
case LayerVoteType::Max:
case LayerVoteType::ExplicitDefault:
case LayerVoteType::ExplicitExact:
+ case LayerVoteType::ExplicitCategory:
return false;
}
}(layer.vote);
@@ -722,7 +750,8 @@
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
- if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+ if (signals.touch && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 &&
+ touchBoostForExplicitExact &&
scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
@@ -838,8 +867,10 @@
}
LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
- layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
- layer->vote != LayerVoteType::ExplicitExact);
+ layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
+ layer->vote != LayerVoteType::ExplicitExact &&
+ layer->vote != LayerVoteType::ExplicitCategory,
+ "Invalid layer vote type for frame rate overrides");
for (auto& [fps, score] : scoredFrameRates) {
constexpr bool isSeamlessSwitch = true;
const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
@@ -1380,6 +1411,27 @@
return mConfig.idleTimerTimeout;
}
+// TODO(b/293651105): Extract category FpsRange mapping to OEM-configurable config.
+FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory category) {
+ switch (category) {
+ case FrameRateCategory::High:
+ return FpsRange{90_Hz, 120_Hz};
+ case FrameRateCategory::Normal:
+ return FpsRange{60_Hz, 90_Hz};
+ case FrameRateCategory::Low:
+ return FpsRange{30_Hz, 60_Hz};
+ case FrameRateCategory::NoPreference:
+ case FrameRateCategory::Default:
+ LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
+ ftl::enum_string(category).c_str());
+ return FpsRange{0_Hz, 0_Hz};
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate category for range: %s",
+ ftl::enum_string(category).c_str());
+ return FpsRange{0_Hz, 0_Hz};
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 7af8d03..41db38d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -147,8 +147,9 @@
// ExactOrMultiple compatibility
ExplicitExact, // Specific refresh rate that was provided by the app with
// Exact compatibility
+ ExplicitCategory, // Specific frame rate category was provided by the app
- ftl_last = ExplicitExact
+ ftl_last = ExplicitCategory
};
// Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -164,6 +165,8 @@
Fps desiredRefreshRate;
// If a seamless mode switch is required.
Seamlessness seamlessness = Seamlessness::Default;
+ // Layer frame rate category. Superseded by desiredRefreshRate.
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
// 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;
@@ -174,12 +177,20 @@
return name == other.name && vote == other.vote &&
isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
seamlessness == other.seamlessness && weight == other.weight &&
- focused == other.focused;
+ focused == other.focused && frameRateCategory == other.frameRateCategory;
}
bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+
+ bool isNoVote() const { return RefreshRateSelector::isNoVote(vote, frameRateCategory); }
};
+ // Returns true if the layer explicitly instructs to not contribute to refresh rate selection.
+ // In other words, true if the layer should be ignored.
+ static bool isNoVote(LayerVoteType vote, FrameRateCategory category) {
+ return vote == LayerVoteType::NoVote || category == FrameRateCategory::NoPreference;
+ }
+
// Global state describing signals that affect refresh rate choice.
struct GlobalSignals {
// Whether the user touched the screen recently. Used to apply touch boost.
@@ -344,6 +355,9 @@
Fps displayFrameRate, GlobalSignals) const
EXCLUDES(mLock);
+ // Gets the FpsRange that the FrameRateCategory represents.
+ static FpsRange getFrameRateCategoryRange(FrameRateCategory category);
+
std::optional<KernelIdleTimerController> kernelIdleTimerController() {
return mConfig.kernelIdleTimerController;
}
@@ -447,6 +461,11 @@
float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
REQUIRES(mLock);
+ // Calculates the score for non-exact matching layer that has LayerVoteType::ExplicitDefault.
+ float calculateNonExactMatchingDefaultLayerScoreLocked(nsecs_t displayPeriod,
+ nsecs_t layerPeriod) const
+ REQUIRES(mLock);
+
void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index d6329e2..19e951a 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -23,6 +23,7 @@
#include <type_traits>
#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
#include <scheduler/Time.h>
namespace android {
@@ -81,6 +82,17 @@
bool valid() const;
};
+// The frame rate category of a Layer.
+enum class FrameRateCategory {
+ Default,
+ NoPreference,
+ Low,
+ Normal,
+ High,
+
+ ftl_last = High
+};
+
static_assert(std::is_trivially_copyable_v<Fps>);
constexpr Fps operator""_Hz(unsigned long long frequency) {