SurfaceFlinger: frame rate heuristic during config change

Some devices might takes a few frames to change the refresh rate.
Calculating the frame rate should ignore frames sent while changing
the refresh rate as those might result in an inaccurate frame rate
due to frame misses.

Bug: 156530990
Test: Running Hay Day and observing refresh rate.
Change-Id: I99f364cd1816c3ce2ba3c3145331eb618dcaea71
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index a1ae35c..d5bebf6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -52,6 +52,8 @@
     // Sets the display size. Client is responsible for synchronization.
     virtual void setDisplayArea(uint32_t displayArea) = 0;
 
+    virtual void setConfigChangePending(bool pending) = 0;
+
     // Marks the layer as active, and records the given state to its history.
     virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
 
@@ -78,6 +80,8 @@
 
     void setDisplayArea(uint32_t /*displayArea*/) override {}
 
+    void setConfigChangePending(bool /*pending*/) override {}
+
     // Marks the layer as active, and records the given state to its history.
     void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
 
@@ -134,6 +138,8 @@
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
 
+    void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
+
     // Marks the layer as active, and records the given state to its history.
     void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
 
@@ -178,6 +184,9 @@
 
     // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
     const bool mUseFrameRatePriority;
+
+    // Whether a config change is in progress or not
+    std::atomic<bool> mConfigChangePending = false;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 120a60f..afc8c4f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -103,7 +103,7 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now);
+    info->setLastPresentTime(presentTime, now, mConfigChangePending);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 255eac6..c16387a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -34,12 +34,15 @@
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, 0.0f}) {}
 
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
+                                     bool pendingConfigChange) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
 
-    FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
+    FrameTimeData frameTime = {.presetTime = lastPresentTime,
+                               .queueTime = mLastUpdatedTime,
+                               .pendingConfigChange = pendingConfigChange};
 
     mFrameTimes.push_back(frameTime);
     if (mFrameTimes.size() > HISTORY_SIZE) {
@@ -80,21 +83,20 @@
     return true;
 }
 
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
-    static constexpr float MARGIN = 1.0f; // 1Hz
-
-    if (!hasEnoughDataForHeuristic()) {
-        ALOGV("Not enough data");
-        return std::nullopt;
-    }
-
-    // Calculate the refresh rate by finding the average delta between frames
+std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
     nsecs_t totalPresentTimeDeltas = 0;
     nsecs_t totalQueueTimeDeltas = 0;
-    auto missingPresentTime = false;
+    bool missingPresentTime = false;
+    int numFrames = 0;
     for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+        // Ignore frames captured during a config change
+        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+            continue;
+        }
+
         totalQueueTimeDeltas +=
                 std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+        numFrames++;
 
         if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
             missingPresentTime = true;
@@ -105,11 +107,6 @@
                 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
     }
 
-    // If there are no presentation timestamps provided we can't calculate the refresh rate
-    if (missingPresentTime && mLastReportedRefreshRate == 0) {
-        return std::nullopt;
-    }
-
     // Calculate the average frame time based on presentation timestamps. If those
     // doesn't exist, we look at the time the buffer was queued only. We can do that only if
     // we calculated a refresh rate based on presentation timestamps in the past. The reason
@@ -117,13 +114,18 @@
     // when implementing render ahead for specific refresh rates. When hwui no longer provides
     // presentation timestamps we look at the queue time to see if the current refresh rate still
     // matches the content.
-    const float averageFrameTime =
+    const auto averageFrameTime =
             static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
-            (mFrameTimes.size() - 1);
+            numFrames;
+    return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
+}
 
-    // Now once we calculated the refresh rate we need to make sure that all the frames we captured
-    // are evenly distributed and we don't calculate the average across some burst of frames.
+bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
     for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+        // Ignore frames captured during a config change
+        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+            continue;
+        }
         const auto presentTimeDeltas = [&] {
             const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
                                                   : (it + 1)->presetTime - it->presetTime;
@@ -131,10 +133,32 @@
         }();
 
         if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
-            return std::nullopt;
+            return false;
         }
     }
 
+    return true;
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+    static constexpr float MARGIN = 1.0f; // 1Hz
+
+    if (!hasEnoughDataForHeuristic()) {
+        ALOGV("Not enough data");
+        return std::nullopt;
+    }
+
+    const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
+
+    // If there are no presentation timestamps provided we can't calculate the refresh rate
+    if (missingPresentTime && mLastReportedRefreshRate == 0) {
+        return std::nullopt;
+    }
+
+    if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
+        return std::nullopt;
+    }
+
     const auto refreshRate = 1e9f / averageFrameTime;
     if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
         mLastReportedRefreshRate = refreshRate;
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 97c7017..ccd6be4 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -63,7 +63,7 @@
     // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -93,11 +93,14 @@
     struct FrameTimeData {
         nsecs_t presetTime; // desiredPresentTime, if provided
         nsecs_t queueTime;  // buffer queue time
+        bool pendingConfigChange;
     };
 
     bool isFrequent(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
     std::optional<float> calculateRefreshRateIfPossible();
+    std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
+    bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const;
 
     const std::string mName;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4eef81d..7c2af23 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -423,6 +423,12 @@
     }
 }
 
+void Scheduler::setConfigChangePending(bool pending) {
+    if (mLayerHistory) {
+        mLayerHistory->setConfigChangePending(pending);
+    }
+}
+
 void Scheduler::chooseRefreshRateForContent() {
     if (!mLayerHistory) return;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 6eabfd2..066e9ca 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -119,6 +119,7 @@
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime);
+    void setConfigChangePending(bool pending);
 
     // Detects content using layer history, and selects a matching refresh rate.
     void chooseRefreshRateForContent();