SF: Improve LayerInfo::calculateAverageFrameTime

This refactors and improves LayerInfo::calculateAverageFrameTime.
The behaviour is changed in two ways:

 * if two consecutive frames are too close to each other we count
   them as one frame and consider the delta between them in
   the total. This gives a better estimation for the average
   refresh rate. See CalculateAverageFrameTimeTest::ignoresSmallPeriods
   which was failing with the previous implementation.

 * if two consecutive frames are too far apart we discard the delta
   between them. This is covered by the test "ignoresLargePeriods".

Fixes: 170476958
Test: atest CalculateAverageFrameTimeTest
Change-Id: If98199bb8198f74c93e93c9996107c021f1bc7ba
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
new file mode 100644
index 0000000..d5c9b57
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfoTest"
+
+#include <gtest/gtest.h>
+
+#include "Fps.h"
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+
+namespace android::scheduler {
+
+class LayerInfoTest : public testing::Test {
+protected:
+    using FrameTimeData = LayerInfo::FrameTimeData;
+
+    void setFrameTimes(const std::deque<FrameTimeData>& frameTimes) {
+        layerInfo.mFrameTimes = frameTimes;
+    }
+
+    void setLastRefreshRate(Fps fps) {
+        layerInfo.mLastRefreshRate.reported = fps;
+        layerInfo.mLastRefreshRate.calculated = fps;
+    }
+
+    auto calculateAverageFrameTime() { return layerInfo.calculateAverageFrameTime(); }
+
+    LayerInfo layerInfo{"TestLayerInfo", LayerHistory::LayerVoteType::Heuristic};
+};
+
+namespace {
+
+TEST_F(LayerInfoTest, prefersPresentTime) {
+    std::deque<FrameTimeData> frameTimes;
+    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
+    constexpr int kNumFrames = 10;
+    for (int i = 1; i <= kNumFrames; i++) {
+        frameTimes.push_back(FrameTimeData{.presentTime = kPeriod * i,
+                                           .queueTime = 0,
+                                           .pendingConfigChange = false});
+    }
+    setFrameTimes(frameTimes);
+    const auto averageFrameTime = calculateAverageFrameTime();
+    ASSERT_TRUE(averageFrameTime.has_value());
+    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+TEST_F(LayerInfoTest, fallbacksToQueueTimeIfNoPresentTime) {
+    std::deque<FrameTimeData> frameTimes;
+    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
+    constexpr int kNumFrames = 10;
+    for (int i = 1; i <= kNumFrames; i++) {
+        frameTimes.push_back(FrameTimeData{.presentTime = 0,
+                                           .queueTime = kPeriod * i,
+                                           .pendingConfigChange = false});
+    }
+    setFrameTimes(frameTimes);
+    setLastRefreshRate(Fps(20.0f)); // Set to some valid value
+    const auto averageFrameTime = calculateAverageFrameTime();
+    ASSERT_TRUE(averageFrameTime.has_value());
+    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+TEST_F(LayerInfoTest, returnsNulloptIfThereWasConfigChange) {
+    std::deque<FrameTimeData> frameTimesWithoutConfigChange;
+    const auto period = Fps(50.0f).getPeriodNsecs();
+    constexpr int kNumFrames = 10;
+    for (int i = 1; i <= kNumFrames; i++) {
+        frameTimesWithoutConfigChange.push_back(FrameTimeData{.presentTime = period * i,
+                                                              .queueTime = period * i,
+                                                              .pendingConfigChange = false});
+    }
+
+    setFrameTimes(frameTimesWithoutConfigChange);
+    ASSERT_TRUE(calculateAverageFrameTime().has_value());
+
+    {
+        // Config change in the first record
+        auto frameTimes = frameTimesWithoutConfigChange;
+        frameTimes[0].pendingConfigChange = true;
+        setFrameTimes(frameTimes);
+        ASSERT_FALSE(calculateAverageFrameTime().has_value());
+    }
+
+    {
+        // Config change in the last record
+        auto frameTimes = frameTimesWithoutConfigChange;
+        frameTimes[frameTimes.size() - 1].pendingConfigChange = true;
+        setFrameTimes(frameTimes);
+        ASSERT_FALSE(calculateAverageFrameTime().has_value());
+    }
+
+    {
+        // Config change in the middle
+        auto frameTimes = frameTimesWithoutConfigChange;
+        frameTimes[frameTimes.size() / 2].pendingConfigChange = true;
+        setFrameTimes(frameTimes);
+        ASSERT_FALSE(calculateAverageFrameTime().has_value());
+    }
+}
+
+// A frame can be recorded twice with very close presentation or queue times.
+// Make sure that this doesn't influence the calculated average FPS.
+TEST_F(LayerInfoTest, ignoresSmallPeriods) {
+    std::deque<FrameTimeData> frameTimes;
+    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
+    constexpr auto kSmallPeriod = Fps(150.0f).getPeriodNsecs();
+    constexpr int kNumIterations = 10;
+    for (int i = 1; i <= kNumIterations; i++) {
+        frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
+                                           .queueTime = 0,
+                                           .pendingConfigChange = false});
+
+        // A duplicate frame
+        frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i + kSmallPeriod,
+                                           .queueTime = 0,
+                                           .pendingConfigChange = false});
+    }
+    setFrameTimes(frameTimes);
+    const auto averageFrameTime = calculateAverageFrameTime();
+    ASSERT_TRUE(averageFrameTime.has_value());
+    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+// There may be a big period of time between two frames. Make sure that
+// this doesn't influence the calculated average FPS.
+TEST_F(LayerInfoTest, ignoresLargePeriods) {
+    std::deque<FrameTimeData> frameTimes;
+    constexpr auto kExpectedFps = Fps(50.0f);
+    constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
+    constexpr auto kLargePeriod = Fps(9.0f).getPeriodNsecs();
+
+    auto record = [&](nsecs_t time) {
+        frameTimes.push_back(
+                FrameTimeData{.presentTime = time, .queueTime = 0, .pendingConfigChange = false});
+    };
+
+    auto time = kExpectedPeriod; // Start with non-zero time.
+    record(time);
+    time += kLargePeriod;
+    record(time);
+    constexpr int kNumIterations = 10;
+    for (int i = 1; i <= kNumIterations; i++) {
+        time += kExpectedPeriod;
+        record(time);
+    }
+
+    setFrameTimes(frameTimes);
+    const auto averageFrameTime = calculateAverageFrameTime();
+    ASSERT_TRUE(averageFrameTime.has_value());
+    const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
+    ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
+            << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+}
+
+} // namespace
+} // namespace android::scheduler