SF: Mutable RefreshRateConfigs
Modify the list of supported refresh rates in RefreshRateConfigs
on hotplug. In order to support this the RefreshRateConfigs class
is refactored to not return references to its internal members,
but instead return copies. This won't be expensive because
the RefreshRate objects which are returned are small.
Bug: 159590486
Test: 1. boot w/o display
2. connect a display which supports multiple refresh rates
3. request a mode switch from an app
4. verify that the switch works
Test: atest libsurfaceflinger_unittest
Test: toggle refresh rate overlay
adb shell service call SurfaceFlinger 1034 i32 1/0
Change-Id: I1588bf004a0a0319a94931adbfb822836703849a
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 35b382e..4677db9 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -38,6 +38,26 @@
toString(layer.seamlessness).c_str(),
to_string(layer.desiredRefreshRate).c_str());
}
+
+std::vector<Fps> constructKnownFrameRates(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+ knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+ // Add all supported refresh rates to the set
+ for (const auto& config : configs) {
+ const auto refreshRate = Fps::fromPeriodNsecs(config->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;
@@ -153,9 +173,9 @@
float score;
};
-const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
- GlobalSignals* outSignalsConsidered) const {
+RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -468,9 +488,20 @@
return bestRefreshRate;
}
-const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
+ std::optional<HwcConfigIndexType> desiredActiveConfigId, bool timerExpired) const {
std::lock_guard lock(mLock);
- return getMinRefreshRateByPolicyLocked();
+
+ const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
+ : *mCurrentRefreshRate;
+ const auto& min = *mMinSupportedRefreshRate;
+
+ if (current != min) {
+ const auto& refreshRate = timerExpired ? min : current;
+ return refreshRate.getFps();
+ }
+
+ return {};
}
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
@@ -486,7 +517,7 @@
return *mPrimaryRefreshRates.front();
}
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getMaxRefreshRateByPolicyLocked();
}
@@ -505,12 +536,12 @@
return *mPrimaryRefreshRates.back();
}
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const {
std::lock_guard lock(mLock);
return *mCurrentRefreshRate;
}
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getCurrentRefreshRateByPolicyLocked();
}
@@ -532,16 +563,23 @@
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId)
: mKnownFrameRates(constructKnownFrameRates(configs)) {
+ updateDisplayConfigs(configs, currentConfigId);
+}
+
+void RefreshRateConfigs::updateDisplayConfigs(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ HwcConfigIndexType currentConfigId) {
+ std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(configs.empty());
LOG_ALWAYS_FATAL_IF(currentConfigId.value() < 0);
LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
+ mRefreshRates.clear();
for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
const auto& config = configs.at(static_cast<size_t>(configId.value()));
+ const auto fps = Fps::fromPeriodNsecs(config->getVsyncPeriod());
mRefreshRates.emplace(configId,
- std::make_unique<RefreshRate>(configId, config,
- Fps::fromPeriodNsecs(
- config->getVsyncPeriod()),
+ std::make_unique<RefreshRate>(configId, config, fps,
RefreshRate::ConstructorTag(0)));
if (configId == currentConfigId) {
mCurrentRefreshRate = mRefreshRates.at(configId).get();
@@ -549,7 +587,7 @@
}
std::vector<const RefreshRate*> sortedConfigs;
- getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedConfigs);
mDisplayManagerPolicy.defaultConfig = currentConfigId;
mMinSupportedRefreshRate = sortedConfigs.front();
mMaxSupportedRefreshRate = sortedConfigs.back();
@@ -566,7 +604,7 @@
constructAvailableRefreshRates();
}
-bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
+bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
// defaultConfig must be a valid config, and within the given refresh rate range.
auto iter = mRefreshRates.find(policy.defaultConfig);
if (iter == mRefreshRates.end()) {
@@ -584,7 +622,7 @@
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
std::lock_guard lock(mLock);
- if (!isPolicyValid(policy)) {
+ if (!isPolicyValidLocked(policy)) {
ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
@@ -599,7 +637,7 @@
status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
std::lock_guard lock(mLock);
- if (policy && !isPolicyValid(*policy)) {
+ if (policy && !isPolicyValidLocked(*policy)) {
return BAD_VALUE;
}
Policy previousPolicy = *getCurrentPolicyLocked();
@@ -635,14 +673,14 @@
return false;
}
-void RefreshRateConfigs::getSortedRefreshRateList(
+void RefreshRateConfigs::getSortedRefreshRateListLocked(
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("getSortedRefreshRateList: config %d added to list policy",
+ ALOGV("getSortedRefreshRateListLocked: config %d added to list policy",
refreshRate->configId.value());
outRefreshRates->push_back(refreshRate.get());
}
@@ -668,8 +706,9 @@
ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
auto filterRefreshRates = [&](Fps min, Fps max, const char* listName,
- std::vector<const RefreshRate*>* outRefreshRates) {
- getSortedRefreshRateList(
+ std::vector<const RefreshRate*>*
+ outRefreshRates) REQUIRES(mLock) {
+ getSortedRefreshRateListLocked(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
const auto& hwcConfig = refreshRate.hwcConfig;
@@ -702,25 +741,6 @@
&mAppRequestRefreshRates);
}
-std::vector<Fps> RefreshRateConfigs::constructKnownFrameRates(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
- std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
- knownFrameRates.reserve(knownFrameRates.size() + configs.size());
-
- // Add all supported refresh rates to the set
- for (const auto& config : configs) {
- const auto refreshRate = Fps::fromPeriodNsecs(config->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;
-}
-
Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
return *mKnownFrameRates.begin();
@@ -740,7 +760,7 @@
RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
std::lock_guard lock(mLock);
- const auto& deviceMin = getMinRefreshRate();
+ const auto& deviceMin = *mMinSupportedRefreshRate;
const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e4bbf7f..4b99145 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -68,8 +68,6 @@
std::shared_ptr<const HWC2::Display::Config> config, Fps fps, ConstructorTag)
: configId(configId), hwcConfig(config), fps(std::move(fps)) {}
- RefreshRate(const RefreshRate&) = delete;
-
HwcConfigIndexType getConfigId() const { return configId; }
nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
@@ -111,27 +109,26 @@
using AllRefreshRatesMapType =
std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
+ struct FpsRange {
+ Fps min{0.0f};
+ Fps max{std::numeric_limits<float>::max()};
+
+ bool operator==(const FpsRange& other) const {
+ return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+ }
+
+ bool operator!=(const FpsRange& other) const { return !(*this == other); }
+
+ std::string toString() const {
+ return base::StringPrintf("[%s %s]", to_string(min).c_str(), to_string(max).c_str());
+ }
+ };
+
struct Policy {
private:
static constexpr int kAllowGroupSwitchingDefault = false;
public:
- struct Range {
- Fps min{0.0f};
- Fps max{std::numeric_limits<float>::max()};
-
- bool operator==(const Range& other) const {
- return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
- }
-
- bool operator!=(const Range& other) const { return !(*this == other); }
-
- std::string toString() const {
- return base::StringPrintf("[%s %s]", to_string(min).c_str(),
- to_string(max).c_str());
- }
- };
-
// The default config, used to ensure we only initiate display config switches within the
// same config group as defaultConfigId's group.
HwcConfigIndexType defaultConfig;
@@ -140,29 +137,29 @@
// The primary refresh rate range represents display manager's general guidance on the
// display configs we'll consider when switching refresh rates. Unless we get an explicit
// signal from an app, we should stay within this range.
- Range primaryRange;
+ FpsRange primaryRange;
// The app request refresh rate range allows us to consider more display configs when
// switching refresh rates. Although we should generally stay within the primary range,
// specific considerations, such as layer frame rate settings specified via the
// setFrameRate() api, may cause us to go outside the primary range. We never go outside the
// app request range. The app request range will be greater than or equal to the primary
// refresh rate range, never smaller.
- Range appRequestRange;
+ FpsRange appRequestRange;
Policy() = default;
- Policy(HwcConfigIndexType defaultConfig, const Range& range)
+ Policy(HwcConfigIndexType defaultConfig, const FpsRange& range)
: Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range)
+ Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const FpsRange& range)
: Policy(defaultConfig, allowGroupSwitching, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
- const Range& appRequestRange)
+ Policy(HwcConfigIndexType defaultConfig, const FpsRange& primaryRange,
+ const FpsRange& appRequestRange)
: Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching,
- const Range& primaryRange, const Range& appRequestRange)
+ const FpsRange& primaryRange, const FpsRange& appRequestRange)
: defaultConfig(defaultConfig),
allowGroupSwitching(allowGroupSwitching),
primaryRange(primaryRange),
@@ -258,35 +255,35 @@
// globalSignals - global state of touch and idle
// outSignalsConsidered - An output param that tells the caller whether the refresh rate was
// chosen based on touch boost and/or idle timer.
- const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
- GlobalSignals* outSignalsConsidered = nullptr) const
+ RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
- // Returns the lowest refresh rate supported by the device. This won't change at runtime.
- const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
+ FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
+ }
- // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
- // uses the primary range, not the app request range.
- const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
-
- // Returns the highest refresh rate supported by the device. This won't change at runtime.
- const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
+ std::optional<Fps> onKernelTimerChanged(std::optional<HwcConfigIndexType> desiredActiveConfigId,
+ bool timerExpired) const EXCLUDES(mLock);
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
- const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+ RefreshRate getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
// Returns the current refresh rate
- const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+ RefreshRate getCurrentRefreshRate() const EXCLUDES(mLock);
// Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
// the policy.
- const RefreshRate& getCurrentRefreshRateByPolicy() const;
+ RefreshRate getCurrentRefreshRateByPolicy() const;
- // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+ // Returns the refresh rate that corresponds to a HwcConfigIndexType. This may change at
// runtime.
- const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+ // TODO(b/159590486) An invalid config id may be given here if the dipslay configs have changed.
+ RefreshRate getRefreshRateFromConfigId(HwcConfigIndexType configId) const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
return *mRefreshRates.at(configId);
};
@@ -302,10 +299,17 @@
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
+ void updateDisplayConfigs(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ HwcConfigIndexType currentConfig) EXCLUDES(mLock);
+
// Returns whether switching configs (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
// differ in resolution.
- bool canSwitch() const { return mRefreshRates.size() > 1; }
+ bool canSwitch() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return mRefreshRates.size() > 1;
+ }
// Class to enumerate options around toggling the kernel timer on and off. We have an option
// for no change to avoid extra calls to kernel.
@@ -334,12 +338,10 @@
friend class RefreshRateConfigsTest;
void constructAvailableRefreshRates() REQUIRES(mLock);
- static std::vector<Fps> constructKnownFrameRates(
- const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
- void getSortedRefreshRateList(
+ void getSortedRefreshRateListLocked(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
- std::vector<const RefreshRate*>* outRefreshRates);
+ std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
// Returns the refresh rate with the highest score in the collection specified from begin
// to end. If there are more than one with the same highest refresh rate, the first one is
@@ -364,7 +366,7 @@
const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
- bool isPolicyValid(const Policy& policy);
+ bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
// Return the display refresh rate divider to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -376,9 +378,9 @@
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
bool isSeamlessSwitch) const REQUIRES(mLock);
- // The list of refresh rates, indexed by display config ID. This must not change after this
+ // The list of refresh rates, indexed by display config ID. This may change after this
// object is initialized.
- AllRefreshRatesMapType mRefreshRates;
+ AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
// The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod
// (the first element is the lowest refresh rate).
@@ -398,9 +400,9 @@
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
// The min and max refresh rates supported by the device.
- // This will not change at runtime.
- const RefreshRate* mMinSupportedRefreshRate;
- const RefreshRate* mMaxSupportedRefreshRate;
+ // This may change at runtime.
+ const RefreshRate* mMinSupportedRefreshRate GUARDED_BY(mLock);
+ const RefreshRate* mMaxSupportedRefreshRate GUARDED_BY(mLock);
mutable std::mutex mLock;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 18c899b..49e3903 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -612,7 +612,7 @@
mFeatures.contentRequirements = summary;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
- auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
frameRateOverridesChanged =
updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
@@ -629,7 +629,7 @@
}
}
if (frameRateChanged) {
- auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate,
consideredSignals.idle ? ConfigEvent::None
: ConfigEvent::Changed);