SurfaceFlinger: Consider resolution when switching refresh rate
Do not change refresh rate across configs with multiple resolutions
Bug: 150237118
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Change-Id: I9b0f355a72343cf120229b98e6ad082ea75d3080
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
index cfbbdfe..8ba4f20 100644
--- a/services/surfaceflinger/Scheduler/HwcStrongTypes.h
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -22,6 +22,5 @@
// Strong types for the different indexes as they are referring to a different base.
using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
-using HwcConfigGroupType = StrongTyping<int, struct HwcConfigGroupTypeTag, Compare>;
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 43883fb..d9aaa05 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -43,7 +43,7 @@
refreshRates.reserve(allRefreshRates.size());
for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->fps);
+ refreshRates.emplace_back(refreshRate->getFps());
}
return refreshRates;
@@ -59,7 +59,7 @@
PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
: PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().fps,
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
// Below defines the threshold when an offset is considered to be negative,
// i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
// < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
@@ -275,7 +275,7 @@
PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
: PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().fps,
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
getProperty("debug.sf.late.sf.duration").value_or(-1),
getProperty("debug.sf.late.app.duration").value_or(-1),
getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 14ef733..5634adb 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -53,7 +53,7 @@
if (explicitContentFramerate != 0) {
contentFramerate = explicitContentFramerate;
} else if (contentFramerate == 0) {
- contentFramerate = round<int>(mMaxSupportedRefreshRate->fps);
+ contentFramerate = round<int>(mMaxSupportedRefreshRate->getFps());
}
ATRACE_INT("ContentFPS", contentFramerate);
@@ -177,7 +177,7 @@
continue;
}
- const auto displayPeriod = scores[i].first->vsyncPeriod;
+ 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 = [&]() {
@@ -309,21 +309,30 @@
mCurrentRefreshRate = mRefreshRates.at(configId).get();
}
-RefreshRateConfigs::RefreshRateConfigs(const std::vector<InputConfig>& configs,
- HwcConfigIndexType currentHwcConfig) {
- init(configs, currentHwcConfig);
-}
-
RefreshRateConfigs::RefreshRateConfigs(
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId) {
- std::vector<InputConfig> inputConfigs;
- for (size_t configId = 0; configId < configs.size(); ++configId) {
- auto configGroup = HwcConfigGroupType(configs[configId]->getConfigGroup());
- inputConfigs.push_back({HwcConfigIndexType(static_cast<int>(configId)), configGroup,
- configs[configId]->getVsyncPeriod()});
+ 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("%2.ffps", fps), fps,
+ RefreshRate::ConstructorTag(0)));
+ if (configId == currentConfigId) {
+ mCurrentRefreshRate = mRefreshRates.at(configId).get();
+ }
}
- init(inputConfigs, currentConfigId);
+
+ std::vector<const RefreshRate*> sortedConfigs;
+ getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ mDisplayManagerPolicy.defaultConfig = currentConfigId;
+ mMinSupportedRefreshRate = sortedConfigs.front();
+ mMaxSupportedRefreshRate = sortedConfigs.back();
+ constructAvailableRefreshRates();
}
bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
@@ -406,10 +415,13 @@
std::sort(outRefreshRates->begin(), outRefreshRates->end(),
[](const auto refreshRate1, const auto refreshRate2) {
- if (refreshRate1->vsyncPeriod != refreshRate2->vsyncPeriod) {
- return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ if (refreshRate1->hwcConfig->getVsyncPeriod() !=
+ refreshRate2->hwcConfig->getVsyncPeriod()) {
+ return refreshRate1->hwcConfig->getVsyncPeriod() >
+ refreshRate2->hwcConfig->getVsyncPeriod();
} else {
- return refreshRate1->configGroup > refreshRate2->configGroup;
+ return refreshRate1->hwcConfig->getConfigGroup() >
+ refreshRate2->hwcConfig->getConfigGroup();
}
});
}
@@ -417,13 +429,20 @@
void RefreshRateConfigs::constructAvailableRefreshRates() {
// Filter configs based on current policy and sort based on vsync period
const Policy* policy = getCurrentPolicyLocked();
- HwcConfigGroupType group = mRefreshRates.at(policy->defaultConfig)->configGroup;
+ const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig)->hwcConfig;
ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
- policy->defaultConfig.value(), group.value(), policy->minRefreshRate,
+ policy->defaultConfig.value(), defaultConfig->getConfigGroup(), policy->minRefreshRate,
policy->maxRefreshRate);
getSortedRefreshRateList(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
- return (policy->allowGroupSwitching || refreshRate.configGroup == group) &&
+ 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(policy->minRefreshRate, policy->maxRefreshRate);
},
&mAvailableRefreshRates);
@@ -440,30 +459,4 @@
policy->maxRefreshRate);
}
-// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
-void RefreshRateConfigs::init(const std::vector<InputConfig>& configs,
- HwcConfigIndexType currentHwcConfig) NO_THREAD_SAFETY_ANALYSIS {
- LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
-
- for (const auto& config : configs) {
- const float fps = 1e9f / config.vsyncPeriod;
- mRefreshRates.emplace(config.configId,
- std::make_unique<RefreshRate>(config.configId, config.vsyncPeriod,
- config.configGroup,
- base::StringPrintf("%2.ffps", fps),
- fps));
- if (config.configId == currentHwcConfig) {
- mCurrentRefreshRate = mRefreshRates.at(config.configId).get();
- }
- }
-
- std::vector<const RefreshRate*> sortedConfigs;
- getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
- mDisplayManagerPolicy.defaultConfig = currentHwcConfig;
- mMinSupportedRefreshRate = sortedConfigs.front();
- mMaxSupportedRefreshRate = sortedConfigs.back();
- constructAvailableRefreshRates();
-}
-
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e749f8f..dea7e90 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -29,6 +29,8 @@
#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
+class RefreshRateConfigsTest;
+
using namespace std::chrono_literals;
enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };
@@ -49,30 +51,27 @@
static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
std::chrono::nanoseconds(800us).count();
- struct RefreshRate {
- // The tolerance within which we consider FPS approximately equals.
- static constexpr float FPS_EPSILON = 0.001f;
+ class RefreshRate {
+ private:
+ // Effectively making the constructor private while allowing
+ // std::make_unique to create the object
+ struct ConstructorTag {
+ explicit ConstructorTag(int) {}
+ };
- RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
- HwcConfigGroupType configGroup, std::string name, float fps)
- : configId(configId),
- vsyncPeriod(vsyncPeriod),
- configGroup(configGroup),
- name(std::move(name)),
- fps(fps) {}
+ public:
+ RefreshRate(HwcConfigIndexType configId,
+ std::shared_ptr<const HWC2::Display::Config> config, std::string name,
+ float fps, ConstructorTag)
+ : configId(configId), hwcConfig(config), name(std::move(name)), fps(fps) {}
RefreshRate(const RefreshRate&) = delete;
- // This config ID corresponds to the position of the config in the vector that is stored
- // on the device.
- const HwcConfigIndexType configId;
- // Vsync period in nanoseconds.
- const nsecs_t vsyncPeriod;
- // This configGroup for the config.
- const HwcConfigGroupType configGroup;
- // Human readable name of the refresh rate.
- const std::string name;
- // Refresh rate in frames per second
- const float fps = 0;
+
+ HwcConfigIndexType getConfigId() const { return configId; }
+ nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
+ int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
+ const std::string& getName() const { return name; }
+ float getFps() const { return fps; }
// Checks whether the fps of this RefreshRate struct is within a given min and max refresh
// rate passed in. FPS_EPSILON is applied to the boundaries for approximation.
@@ -81,11 +80,27 @@
}
bool operator!=(const RefreshRate& other) const {
- return configId != other.configId || vsyncPeriod != other.vsyncPeriod ||
- configGroup != other.configGroup;
+ return configId != other.configId || hwcConfig != other.hwcConfig;
}
bool operator==(const RefreshRate& other) const { return !(*this != other); }
+
+ private:
+ friend RefreshRateConfigs;
+ friend RefreshRateConfigsTest;
+
+ // The tolerance within which we consider FPS approximately equals.
+ static constexpr float FPS_EPSILON = 0.001f;
+
+ // This config ID corresponds to the position of the config in the vector that is stored
+ // on the device.
+ const HwcConfigIndexType configId;
+ // The config itself
+ std::shared_ptr<const HWC2::Display::Config> hwcConfig;
+ // Human readable name of the refresh rate.
+ const std::string name;
+ // Refresh rate in frames per second
+ const float fps = 0;
};
using AllRefreshRatesMapType =
@@ -208,20 +223,10 @@
// Stores the current configId the device operates at
void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
- struct InputConfig {
- HwcConfigIndexType configId = HwcConfigIndexType(0);
- HwcConfigGroupType configGroup = HwcConfigGroupType(0);
- nsecs_t vsyncPeriod = 0;
- };
-
- RefreshRateConfigs(const std::vector<InputConfig>& configs,
- HwcConfigIndexType currentHwcConfig);
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
private:
- void init(const std::vector<InputConfig>& configs, HwcConfigIndexType currentHwcConfig);
-
void constructAvailableRefreshRates() REQUIRES(mLock);
void getSortedRefreshRateList(
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index e44cd52..66d4a03 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -78,10 +78,10 @@
// Multiple configs may map to the same name, e.g. "60fps". Add the
// times for such configs together.
for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] = 0;
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getName()] = 0;
}
for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] += time;
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getName()] += time;
}
totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
@@ -115,7 +115,7 @@
}
mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
fps = static_cast<uint32_t>(std::round(
- mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps));
+ mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).getFps()));
} else {
mScreenOffTime += timeElapsedMs;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index cd6075f..9a9523f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -332,7 +332,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().vsyncPeriod);
+ resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod());
}
}
@@ -389,34 +389,34 @@
// keep the layer history, since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
if (!mUseContentDetection) {
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
- mRefreshRateConfigs.getMaxRefreshRate().fps,
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
+ mRefreshRateConfigs.getMaxRefreshRate().getFps(),
scheduler::LayerHistory::LayerVoteType::NoVote);
return;
}
// In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
if (!mUseContentDetectionV2) {
- const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+ const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
? lowFps
- : mRefreshRateConfigs.getMaxRefreshRate().fps;
+ : mRefreshRateConfigs.getMaxRefreshRate().getFps();
mLayerHistory->registerLayer(layer, lowFps, highFps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
- mRefreshRateConfigs.getMaxRefreshRate().fps,
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
+ mRefreshRateConfigs.getMaxRefreshRate().getFps(),
scheduler::LayerHistory::LayerVoteType::Min);
} else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
- mRefreshRateConfigs.getMaxRefreshRate().fps,
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
+ mRefreshRateConfigs.getMaxRefreshRate().getFps(),
scheduler::LayerHistory::LayerVoteType::NoVote);
} else {
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
- mRefreshRateConfigs.getMaxRefreshRate().fps,
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
+ mRefreshRateConfigs.getMaxRefreshRate().getFps(),
scheduler::LayerHistory::LayerVoteType::Heuristic);
}
}
@@ -503,12 +503,13 @@
// magic number
const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
- if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+ if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
- resyncToHardwareVsync(true /* makeAvailable */, refreshRate.vsyncPeriod);
- } else if (state == TimerState::Expired && refreshRate.fps <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+ resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
+ } else if (state == TimerState::Expired &&
+ refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
// need to update the DispSync model anyway.
@@ -580,30 +581,31 @@
if (mDisplayPowerTimer &&
(!mFeatures.isDisplayPowerStateNormal ||
mFeatures.displayPowerTimer == TimerState::Reset)) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
}
if (!mUseContentDetectionV2) {
// As long as touch is active we want to be in performance mode.
if (mTouchTimer && mFeatures.touch == TouchState::Active) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
}
}
// If timer has expired as it means there is no new content on the screen.
if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
- return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
+ return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
}
if (!mUseContentDetectionV2) {
// If content detection is off we choose performance as we don't know the content fps.
if (mFeatures.contentDetectionV1 == ContentDetectionState::Off) {
// NOTE: V1 always calls this, but this is not a default behavior for V2.
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
}
// Content detection is on, find the appropriate refresh rate with minimal error
- return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
+ return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
+ .getConfigId();
}
bool touchConsidered;
@@ -613,7 +615,7 @@
mTouchTimer &&
mFeatures.touch == TouchState::Active,
&touchConsidered)
- .configId;
+ .getConfigId();
if (touchConsidered) {
// Clear layer history if refresh rate was selected based on touch to allow
// the hueristic to pick up with the new rate.