SurfaceFlinger: use config groups
Composer 2.4 adds a new attribute for configs groups. This change
groups configs according to their group and store them in
RefreshRateConfigs.
Test: rev up composer to 2.4 and test refresh rate switching
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Test: adb shell /data/nativetest64/SurfaceFlinger_test/SurfaceFlinger_test
Bug: 141329414
Fixes: 139751853
Change-Id: Ic0bcd3da4bf6b73efa11a60c2594948ce030362f
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 7dc98cc..23fb96a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -13,135 +13,169 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+// #define LOG_NDEBUG 0
#include "RefreshRateConfigs.h"
namespace android::scheduler {
+
+using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
// Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
// from multiple threads. This can only be called if refreshRateSwitching() returns true.
// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
// baking them in.
-const std::map<RefreshRateType, RefreshRate>& RefreshRateConfigs::getRefreshRateMap() const {
- LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
- return mRefreshRateMap;
-}
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFramerate) const {
+ std::lock_guard lock(mLock);
+ // Find the appropriate refresh rate with minimal error
+ auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
+ [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
+ return std::abs(lhs->fps - contentFramerate) <
+ std::abs(rhs->fps - contentFramerate);
+ });
-const RefreshRate& RefreshRateConfigs::getRefreshRateFromType(RefreshRateType type) const {
- if (!mRefreshRateSwitchingSupported) {
- return getCurrentRefreshRate().second;
- } else {
- auto refreshRate = mRefreshRateMap.find(type);
- LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
- return refreshRate->second;
- }
-}
+ // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+ // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+ // align well with both
+ const RefreshRate* bestSoFar = *iter;
+ constexpr float MARGIN = 0.05f;
+ float ratio = (*iter)->fps / contentFramerate;
+ if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+ while (iter != mAvailableRefreshRates.cend()) {
+ ratio = (*iter)->fps / contentFramerate;
-std::pair<RefreshRateType, const RefreshRate&> RefreshRateConfigs::getCurrentRefreshRate() const {
- int currentConfig = mCurrentConfig;
- if (mRefreshRateSwitchingSupported) {
- for (const auto& [type, refresh] : mRefreshRateMap) {
- if (refresh.configId == currentConfig) {
- return {type, refresh};
+ if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+ bestSoFar = *iter;
+ break;
}
- }
- LOG_ALWAYS_FATAL();
- }
- return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
-}
-
-const RefreshRate& RefreshRateConfigs::getRefreshRateFromConfigId(int configId) const {
- LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
- return mRefreshRates[configId];
-}
-
-RefreshRateType RefreshRateConfigs::getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
- if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
-
- for (const auto& [type, refreshRate] : mRefreshRateMap) {
- if (refreshRate.hwcId == hwcId) {
- return type;
+ ++iter;
}
}
- return RefreshRateType::DEFAULT;
+ return *bestSoFar;
}
-void RefreshRateConfigs::setCurrentConfig(int config) {
- LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
- mCurrentConfig = config;
+const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
+ return mRefreshRates;
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.front();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.back();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return *mCurrentRefreshRate;
+}
+
+void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+ std::lock_guard lock(mLock);
+ mCurrentRefreshRate = &mRefreshRates.at(configId);
}
RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
- const std::vector<InputConfig>& configs, int currentConfig) {
- init(refreshRateSwitching, configs, currentConfig);
+ const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig)
+ : mRefreshRateSwitching(refreshRateSwitching) {
+ init(configs, currentHwcConfig);
}
RefreshRateConfigs::RefreshRateConfigs(
bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig) {
+ HwcConfigIndexType currentConfigId)
+ : mRefreshRateSwitching(refreshRateSwitching) {
std::vector<InputConfig> inputConfigs;
- for (const auto& config : configs) {
- inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
+ for (auto configId = HwcConfigIndexType(0); configId < HwcConfigIndexType(configs.size());
+ ++configId) {
+ auto configGroup = HwcConfigGroupType(configs[configId.value()]->getConfigGroup());
+ inputConfigs.push_back(
+ {configId, configGroup, configs[configId.value()]->getVsyncPeriod()});
}
- init(refreshRateSwitching, inputConfigs, currentConfig);
+ init(inputConfigs, currentConfigId);
}
-void RefreshRateConfigs::init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- mRefreshRateSwitchingSupported = refreshRateSwitching;
+void RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+ float maxRefreshRate) {
+ std::lock_guard lock(mLock);
+ mCurrentGroupId = mRefreshRates.at(defaultConfigId).configGroup;
+ mMinRefreshRateFps = minRefreshRate;
+ mMaxRefreshRateFps = maxRefreshRate;
+ constructAvailableRefreshRates();
+}
+
+void RefreshRateConfigs::getSortedRefreshRateList(
+ 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",
+ refreshRate.configId.value());
+ outRefreshRates->push_back(&refreshRate);
+ }
+ }
+
+ std::sort(outRefreshRates->begin(), outRefreshRates->end(),
+ [](const auto refreshRate1, const auto refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+}
+
+void RefreshRateConfigs::constructAvailableRefreshRates() {
+ // Filter configs based on current policy and sort based on vsync period
+ ALOGV("constructRefreshRateMap: group %d min %.2f max %.2f", mCurrentGroupId.value(),
+ mMinRefreshRateFps, mMaxRefreshRateFps);
+ getSortedRefreshRateList(
+ [this](const RefreshRate& refreshRate) REQUIRES(mLock) {
+ return refreshRate.configGroup == mCurrentGroupId &&
+ refreshRate.fps >= mMinRefreshRateFps &&
+ refreshRate.fps <= mMaxRefreshRateFps;
+ },
+ &mAvailableRefreshRates);
+}
+
+// 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(currentConfig >= configs.size());
- mCurrentConfig = currentConfig;
+ LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
- auto buildRefreshRate = [&](int configId) -> RefreshRate {
- const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
- const float fps = 1e9 / vsyncPeriod;
- return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
- vsyncPeriod, configs[configId].hwcId};
+ auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
+ const float fps = 1e9f / config.vsyncPeriod;
+ return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
+ base::StringPrintf("%2.ffps", fps), fps);
};
- for (int i = 0; i < configs.size(); ++i) {
- mRefreshRates.push_back(buildRefreshRate(i));
+ for (const auto& config : configs) {
+ mRefreshRates.emplace(config.configId, buildRefreshRate(config));
+ if (config.configId == currentHwcConfig) {
+ mCurrentRefreshRate = &mRefreshRates.at(config.configId);
+ mCurrentGroupId = config.configGroup;
+ }
}
- if (!mRefreshRateSwitchingSupported) return;
-
- auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
- if (configs.size() < 2) {
- return {};
- }
-
- std::vector<const RefreshRate*> sortedRefreshRates;
- for (const auto& refreshRate : mRefreshRates) {
- sortedRefreshRates.push_back(&refreshRate);
- }
- std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
- [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
- return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
- });
-
- // When the configs are ordered by the resync rate, we assume that
- // the first one is DEFAULT and the second one is PERFORMANCE,
- // i.e. the higher rate.
- if (sortedRefreshRates[0]->vsyncPeriod == 0 || sortedRefreshRates[1]->vsyncPeriod == 0) {
- return {};
- }
-
- return std::pair<int, int>(sortedRefreshRates[0]->configId,
- sortedRefreshRates[1]->configId);
- };
-
- auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
- if (!defaultAndPerfConfigs) {
- mRefreshRateSwitchingSupported = false;
- return;
- }
-
- mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
- mRefreshRateMap[RefreshRateType::PERFORMANCE] = mRefreshRates[defaultAndPerfConfigs->second];
+ std::vector<const RefreshRate*> sortedConfigs;
+ getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ mMinSupportedRefreshRate = sortedConfigs.front();
+ mMaxSupportedRefreshRate = sortedConfigs.back();
+ constructAvailableRefreshRates();
}
-} // namespace android::scheduler
\ No newline at end of file
+} // namespace android::scheduler