SurfaceFlinger: allow switching when layers vote to refresh rate

This CL is a refinement of the refresh rate switching algorithm
to allow refresh rate switching even if some of the layers voted
explicitly for a refresh rate.

Test: Run ExoPlayer demo app and scroll the list while playing a video
Bug: 147516364
Change-Id: Id01ff8477804bba9e859545e20b05eeb1ec0d319
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index d1de737..b876ccd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -23,8 +23,6 @@
 #include <chrono>
 #include <cmath>
 
-using namespace std::chrono_literals;
-
 namespace android::scheduler {
 
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
@@ -84,14 +82,31 @@
     return *bestSoFar;
 }
 
+std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
+                                                                 nsecs_t displayPeriod) const {
+    auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
+    if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
+        std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+        displayFramesQuot++;
+        displayFramesRem = 0;
+    }
+
+    return {displayFramesQuot, displayFramesRem};
+}
+
 const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
-        const std::vector<LayerRequirement>& layers) const {
-    constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count();
+        const std::vector<LayerRequirement>& layers, bool touchActive) const {
     ATRACE_CALL();
     ALOGV("getRefreshRateForContent %zu layers", layers.size());
 
     std::lock_guard lock(mLock);
 
+    // For now if the touch is active return the peak refresh rate
+    // This should be optimized to consider other layers as well.
+    if (touchActive) {
+        return *mAvailableRefreshRates.back();
+    }
+
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
@@ -115,11 +130,6 @@
         return *mAvailableRefreshRates.front();
     }
 
-    // If we have some Max layers and no Explicit we should return Max
-    if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) {
-        return *mAvailableRefreshRates.back();
-    }
-
     // Find the best refresh rate based on score
     std::vector<std::pair<const RefreshRate*, float>> scores;
     scores.reserve(mAvailableRefreshRates.size());
@@ -130,67 +140,85 @@
 
     for (const auto& layer : layers) {
         ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
-        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
-            layer.vote == LayerVoteType::Max) {
+        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
             continue;
         }
 
-        // Adjust the weight in case we have explicit layers. The priority is:
-        //  - ExplicitExactOrMultiple
-        //  - ExplicitDefault
-        //  - Heuristic
         auto weight = layer.weight;
-        if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) {
-            if (layer.vote == LayerVoteType::Heuristic) {
-                weight /= 2.f;
-            }
-        }
 
-        if (explicitExactOrMultipleVoteLayers > 0) {
-            if (layer.vote == LayerVoteType::Heuristic ||
-                layer.vote == LayerVoteType::ExplicitDefault) {
-                weight /= 2.f;
+        for (auto i = 0u; i < scores.size(); i++) {
+            // If the layer wants Max, give higher score to the higher refresh rate
+            if (layer.vote == LayerVoteType::Max) {
+                const auto ratio = scores[i].first->fps / scores.back().first->fps;
+                // use ratio^2 to get a lower score the more we get further from peak
+                const auto layerScore = ratio * ratio;
+                ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
+                      scores[i].first->name.c_str(), layerScore);
+                scores[i].second += weight * layerScore;
+                continue;
             }
-        }
 
-        for (auto& [refreshRate, overallScore] : scores) {
-            const auto displayPeriod = refreshRate->vsyncPeriod;
+            const auto displayPeriod = scores[i].first->vsyncPeriod;
             const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
+            if (layer.vote == LayerVoteType::ExplicitDefault) {
+                const auto layerScore = [&]() {
+                    const auto [displayFramesQuot, displayFramesRem] =
+                            getDisplayFrames(layerPeriod, displayPeriod);
+                    if (displayFramesQuot == 0) {
+                        // Layer desired refresh rate is higher the display rate.
+                        return static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod);
+                    }
 
-            // Calculate how many display vsyncs we need to present a single frame for this layer
-            auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
-            if (displayFramesRem <= MARGIN ||
-                std::abs(displayFramesRem - displayPeriod) <= MARGIN) {
-                displayFramesQuot++;
-                displayFramesRem = 0;
+                    return 1.0f -
+                            (static_cast<float>(displayFramesRem) /
+                             static_cast<float>(layerPeriod));
+                }();
+
+                ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
+                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
+                      layerScore);
+                scores[i].second += weight * layerScore;
+                continue;
             }
 
-            float layerScore;
-            static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
-            if (displayFramesRem == 0) {
-                // Layer desired refresh rate matches the display rate.
-                layerScore = weight * 1.0f;
-            } else if (displayFramesQuot == 0) {
-                // Layer desired refresh rate is higher the display rate.
-                layerScore = weight *
-                        (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
-                        (1.0f / (MAX_FRAMES_TO_FIT + 1));
-            } else {
-                // Layer desired refresh rate is lower the display rate. Check how well it fits the
-                // cadence
-                auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
-                int iter = 2;
-                while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
-                    diff = diff - (displayPeriod - diff);
-                    iter++;
-                }
+            if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+                layer.vote == LayerVoteType::Heuristic) {
+                const auto layerScore = [&]() {
+                    // Calculate how many display vsyncs we need to present a single frame for this
+                    // layer
+                    const auto [displayFramesQuot, displayFramesRem] =
+                            getDisplayFrames(layerPeriod, displayPeriod);
+                    static constexpr size_t MAX_FRAMES_TO_FIT =
+                            10; // Stop calculating when score < 0.1
+                    if (displayFramesRem == 0) {
+                        // Layer desired refresh rate matches the display rate.
+                        return 1.0f;
+                    }
 
-                layerScore = weight * (1.0f / iter);
+                    if (displayFramesQuot == 0) {
+                        // Layer desired refresh rate is higher the display rate.
+                        return (static_cast<float>(layerPeriod) /
+                                static_cast<float>(displayPeriod)) *
+                                (1.0f / (MAX_FRAMES_TO_FIT + 1));
+                    }
+
+                    // Layer desired refresh rate is lower the display rate. Check how well it fits
+                    // the cadence
+                    auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
+                    int iter = 2;
+                    while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+                        diff = diff - (displayPeriod - diff);
+                        iter++;
+                    }
+
+                    return 1.0f / iter;
+                }();
+                ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f",
+                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
+                      layerScore);
+                scores[i].second += weight * layerScore;
+                continue;
             }
-
-            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight,
-                  1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
-            overallScore += layerScore;
         }
     }