Merge "SF: Create a cicular buffer structure to store layer information."
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9abb040..088c256 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -139,6 +139,7 @@
"Scheduler/DispSyncSource.cpp",
"Scheduler/EventControlThread.cpp",
"Scheduler/EventThread.cpp",
+ "Scheduler/LayerHistory.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/Scheduler.cpp",
"StartPropertySetThread.cpp",
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
new file mode 100644
index 0000000..e944eb9
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "LayerHistory.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+LayerHistory::LayerHistory() {}
+
+LayerHistory::~LayerHistory() = default;
+
+void LayerHistory::insert(const std::string layerName, nsecs_t presentTime) {
+ mElements[mCounter].insert(std::make_pair(layerName, presentTime));
+}
+
+void LayerHistory::incrementCounter() {
+ mCounter++;
+ mCounter = mCounter % ARRAY_SIZE;
+ mElements[mCounter].clear();
+}
+
+const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
+ return mElements.at(index);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
new file mode 100644
index 0000000..1a7f9cd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <array>
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+/*
+ * This class represents a circular buffer in which we keep layer history for
+ * the past ARRAY_SIZE frames. Each time, a signal for new frame comes, the counter
+ * gets incremented and includes all the layers that are requested to draw in that
+ * frame.
+ *
+ * Once the buffer reaches the end of the array, it starts overriding the elements
+ * at the beginning of the array.
+ */
+class LayerHistory {
+public:
+ LayerHistory();
+ ~LayerHistory();
+
+ // Method for inserting layers and their requested present time into the ring buffer.
+ // The elements are going to be inserted into an unordered_map at the position of
+ // mCounter.
+ void insert(const std::string layerName, nsecs_t presentTime);
+ // Method for incrementing the current slot in the ring buffer. It also clears the
+ // unordered_map, if it was created previously.
+ void incrementCounter();
+ // Returns unordered_map at the given at index.
+ const std::unordered_map<std::string, nsecs_t>& get(size_t index) const;
+
+private:
+ size_t mCounter = 0;
+ static constexpr size_t ARRAY_SIZE = 30;
+ std::array<std::unordered_map<std::string, nsecs_t>, ARRAY_SIZE> mElements;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 6fe52d3..58f1e9c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -29,6 +29,7 @@
"DisplayTransactionTest.cpp",
"EventControlThreadTest.cpp",
"EventThreadTest.cpp",
+ "LayerHistoryTest.cpp",
"SchedulerTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplaySurface.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
new file mode 100644
index 0000000..b518f10
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -0,0 +1,144 @@
+#undef LOG_TAG
+#define LOG_TAG "LayerHistoryUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include "Scheduler/LayerHistory.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android {
+
+class LayerHistoryTest : public testing::Test {
+public:
+ LayerHistoryTest();
+ ~LayerHistoryTest() override;
+
+protected:
+ std::unique_ptr<LayerHistory> mLayerHistory;
+};
+
+LayerHistoryTest::LayerHistoryTest() {
+ mLayerHistory = std::make_unique<LayerHistory>();
+}
+LayerHistoryTest::~LayerHistoryTest() {}
+
+namespace {
+TEST_F(LayerHistoryTest, simpleInsertAndGet) {
+ mLayerHistory->insert("TestLayer", 0);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
+ EXPECT_EQ(1, testMap.size());
+ auto element = testMap.find("TestLayer");
+ EXPECT_EQ("TestLayer", element->first);
+ EXPECT_EQ(0, element->second);
+
+ // Testing accessing object at an empty container will return an empty map.
+ const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
+ EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, multipleInserts) {
+ mLayerHistory->insert("TestLayer0", 0);
+ mLayerHistory->insert("TestLayer1", 1);
+ mLayerHistory->insert("TestLayer2", 2);
+ mLayerHistory->insert("TestLayer3", 3);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
+ // Because the counter was not incremented, all elements were inserted into the first
+ // container.
+ EXPECT_EQ(4, testMap.size());
+ auto element = testMap.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
+
+ element = testMap.find("TestLayer1");
+ EXPECT_EQ("TestLayer1", element->first);
+ EXPECT_EQ(1, element->second);
+
+ element = testMap.find("TestLayer2");
+ EXPECT_EQ("TestLayer2", element->first);
+ EXPECT_EQ(2, element->second);
+
+ element = testMap.find("TestLayer3");
+ EXPECT_EQ("TestLayer3", element->first);
+ EXPECT_EQ(3, element->second);
+
+ // Testing accessing object at an empty container will return an empty map.
+ const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
+ EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, incrementingCounter) {
+ mLayerHistory->insert("TestLayer0", 0);
+ mLayerHistory->incrementCounter();
+ mLayerHistory->insert("TestLayer1", 1);
+ mLayerHistory->insert("TestLayer2", 2);
+ mLayerHistory->incrementCounter();
+ mLayerHistory->insert("TestLayer3", 3);
+
+ // Because the counter was incremented, the elements were inserted into different
+ // containers.
+ const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+ EXPECT_EQ(1, testMap1.size());
+ auto element = testMap1.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
+ EXPECT_EQ(2, testMap2.size());
+ element = testMap2.find("TestLayer1");
+ EXPECT_EQ("TestLayer1", element->first);
+ EXPECT_EQ(1, element->second);
+ element = testMap2.find("TestLayer2");
+ EXPECT_EQ("TestLayer2", element->first);
+ EXPECT_EQ(2, element->second);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
+ EXPECT_EQ(1, testMap3.size());
+ element = testMap3.find("TestLayer3");
+ EXPECT_EQ("TestLayer3", element->first);
+ EXPECT_EQ(3, element->second);
+
+ // Testing accessing object at an empty container will return an empty map.
+ const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
+ EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, clearTheMap) {
+ mLayerHistory->insert("TestLayer0", 0);
+ mLayerHistory->incrementCounter();
+
+ const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+ EXPECT_EQ(1, testMap1.size());
+ auto element = testMap1.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
+
+ // The array currently contains 30 elements.
+ for (int i = 1; i < 30; ++i) {
+ mLayerHistory->insert("TestLayer0", i);
+ mLayerHistory->incrementCounter();
+ }
+ // Expect the map to be cleared.
+ const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(0);
+ EXPECT_EQ(0, testMap2.size());
+
+ mLayerHistory->insert("TestLayer30", 30);
+ const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(0);
+ element = testMap3.find("TestLayer30");
+ EXPECT_EQ("TestLayer30", element->first);
+ EXPECT_EQ(30, element->second);
+ // The original element in this location does not exist anymore.
+ element = testMap3.find("TestLayer0");
+ EXPECT_EQ(testMap3.end(), element);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file