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