Pass fps data to flattener

Use this extra data in determining if a layer should be considered inactive for flattening.
The effect is that sub 1 fps layers are cached immediately upon updating.

Test: Unit tests added, and confirmation of the intended effect on R6 during camera recording using
dumpsys surfaceflinger (expected all CameraLauncher layers to be device composited)

Bug: b/192271493

Change-Id: I06f2dd0b3256da5699ffca7347285dc8cf52713c
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 84e3548..74a2ca7 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,42 +84,38 @@
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
     std::lock_guard lock(mLock);
-    for (const auto& info : mLayerInfos) {
-        LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
-    }
+    LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first !=
+                                LayerHistory::layerStatus::NotFound,
+                        "%s already registered", layer->getName().c_str());
     auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
-    mLayerInfos.emplace_back(layer, std::move(info));
+
+    // The layer can be placed on either map, it is assumed that partitionLayers() will be called
+    // to correct them.
+    mInactiveLayerInfos.insert({layer->getSequence(), std::make_pair(layer, std::move(info))});
 }
 
 void LayerHistory::deregisterLayer(Layer* layer) {
     std::lock_guard lock(mLock);
-
-    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
-                                 [layer](const auto& pair) { return pair.first == layer; });
-    LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
-
-    const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
-    if (i < mActiveLayersEnd) {
-        mActiveLayersEnd--;
+    if (!mActiveLayerInfos.erase(layer->getSequence())) {
+        if (!mInactiveLayerInfos.erase(layer->getSequence())) {
+            LOG_ALWAYS_FATAL("%s: unknown layer %p", __FUNCTION__, layer);
+        }
     }
-    const size_t last = mLayerInfos.size() - 1;
-    std::swap(mLayerInfos[i], mLayerInfos[last]);
-    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
 }
 
 void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
                           LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
+    auto id = layer->getSequence();
 
-    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
-                                 [layer](const auto& pair) { return pair.first == layer; });
-    if (it == mLayerInfos.end()) {
+    auto [found, layerPair] = findLayer(id);
+    if (found == LayerHistory::layerStatus::NotFound) {
         // Offscreen layer
         ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
         return;
     }
 
-    const auto& info = it->second;
+    const auto& info = layerPair->second;
     const auto layerProps = LayerInfo::LayerProps{
             .visible = layer->isVisible(),
             .bounds = layer->getBounds(),
@@ -131,9 +127,10 @@
     info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
     // Activate layer if inactive.
-    if (const auto end = activeLayers().end(); it >= end) {
-        std::iter_swap(it, end);
-        mActiveLayersEnd++;
+    if (found == LayerHistory::layerStatus::LayerInInactiveMap) {
+        mActiveLayerInfos.insert(
+                {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+        mInactiveLayerInfos.erase(id);
     }
 }
 
@@ -145,7 +142,8 @@
 
     partitionLayers(now);
 
-    for (const auto& [layer, info] : activeLayers()) {
+    for (const auto& [key, value] : mActiveLayerInfos) {
+        auto& info = value.second;
         const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
         const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
         ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
@@ -179,12 +177,29 @@
 void LayerHistory::partitionLayers(nsecs_t now) {
     const nsecs_t threshold = getActiveLayerThreshold(now);
 
-    // Collect expired and inactive layers after active layers.
-    size_t i = 0;
-    while (i < mActiveLayersEnd) {
-        auto& [layerUnsafe, info] = mLayerInfos[i];
+    // iterate over inactive map
+    LayerInfos::iterator it = mInactiveLayerInfos.begin();
+    while (it != mInactiveLayerInfos.end()) {
+        auto& [layerUnsafe, info] = it->second;
         if (isLayerActive(*info, threshold)) {
-            i++;
+            // move this to the active map
+
+            mActiveLayerInfos.insert({it->first, std::move(it->second)});
+            it = mInactiveLayerInfos.erase(it);
+        } else {
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+            }
+            info->onLayerInactive(now);
+            it++;
+        }
+    }
+
+    // iterate over active map
+    it = mActiveLayerInfos.begin();
+    while (it != mActiveLayerInfos.end()) {
+        auto& [layerUnsafe, info] = it->second;
+        if (isLayerActive(*info, threshold)) {
             // Set layer vote if set
             const auto frameRate = info->getSetFrameRateVote();
             const auto voteType = [&]() {
@@ -206,30 +221,68 @@
             } else {
                 info->resetLayerVote();
             }
-            continue;
-        }
 
-        if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+            it++;
+        } else {
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+            }
+            info->onLayerInactive(now);
+            // move this to the inactive map
+            mInactiveLayerInfos.insert({it->first, std::move(it->second)});
+            it = mActiveLayerInfos.erase(it);
         }
-
-        info->onLayerInactive(now);
-        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
     }
 }
 
 void LayerHistory::clear() {
     std::lock_guard lock(mLock);
-
-    for (const auto& [layer, info] : activeLayers()) {
-        info->clearHistory(systemTime());
+    for (const auto& [key, value] : mActiveLayerInfos) {
+        value.second->clearHistory(systemTime());
     }
 }
 
 std::string LayerHistory::dump() const {
     std::lock_guard lock(mLock);
-    return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
-                              mActiveLayersEnd);
+    return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+                              mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
+                              mActiveLayerInfos.size());
+}
+
+float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const {
+    std::lock_guard lock(mLock);
+    auto [found, layerPair] = findLayer(id);
+    if (found != LayerHistory::layerStatus::NotFound) {
+        return layerPair->second->getFps(now).getValue();
+    }
+    return 0.f;
+}
+
+std::pair<LayerHistory::layerStatus, LayerHistory::LayerPair*> LayerHistory::findLayer(int32_t id) {
+    // the layer could be in either the active or inactive map, try both
+    auto it = mActiveLayerInfos.find(id);
+    if (it != mActiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+    }
+    it = mInactiveLayerInfos.find(id);
+    if (it != mInactiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+    }
+    return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
+}
+
+std::pair<LayerHistory::layerStatus, const LayerHistory::LayerPair*> LayerHistory::findLayer(
+        int32_t id) const {
+    // the layer could be in either the active or inactive map, try both
+    auto it = mActiveLayerInfos.find(id);
+    if (it != mActiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+    }
+    it = mInactiveLayerInfos.find(id);
+    if (it != mInactiveLayerInfos.end()) {
+        return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+    }
+    return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 8d56951..cc55700 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -20,6 +20,7 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
+#include <map>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -72,34 +73,43 @@
     void deregisterLayer(Layer*);
     std::string dump() const;
 
+    // return the frames per second of the layer with the given sequence id.
+    float getLayerFramerate(nsecs_t now, int32_t id) const;
+
 private:
     friend class LayerHistoryTest;
     friend class TestableScheduler;
 
     using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
-    using LayerInfos = std::vector<LayerPair>;
+    // keyed by id as returned from Layer::getSequence()
+    using LayerInfos = std::unordered_map<int32_t, LayerPair>;
 
-    struct ActiveLayers {
-        LayerInfos& infos;
-        const size_t index;
+    // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive
+    // layers to mInactiveLayerInfos.
+    // worst case time complexity is O(2 * inactive + active)
+    void partitionLayers(nsecs_t now) REQUIRES(mLock);
 
-        auto begin() { return infos.begin(); }
-        auto end() { return begin() + static_cast<long>(index); }
+    enum class layerStatus {
+        NotFound,
+        LayerInActiveMap,
+        LayerInInactiveMap,
     };
 
-    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
-
-    // Iterates over layers in a single pass, swapping pairs such that active layers precede
-    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
-    // truncating after inactive layers.
-    void partitionLayers(nsecs_t now) REQUIRES(mLock);
+    // looks up a layer by sequence id in both layerInfo maps.
+    // The first element indicates if and where the item was found
+    std::pair<layerStatus, LayerHistory::LayerPair*> findLayer(int32_t id) REQUIRES(mLock);
+    std::pair<layerStatus, const LayerHistory::LayerPair*> findLayer(int32_t id) const
+            REQUIRES(mLock);
 
     mutable std::mutex mLock;
 
-    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
-    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
-    LayerInfos mLayerInfos GUARDED_BY(mLock);
-    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+    // Partitioned into two maps to facility two kinds of retrieval:
+    // 1. retrieval of a layer by id (attempt lookup in both maps)
+    // 2. retrieval of all active layers (iterate that map)
+    // The partitioning is allowed to become out of date but calling partitionLayers refreshes the
+    // validity of each map.
+    LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
+    LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
 
     uint32_t mDisplayArea = 0;
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index ae61eeb..943615c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -75,12 +75,16 @@
 }
 
 bool LayerInfo::isFrequent(nsecs_t now) const {
+    using fps_approx_ops::operator>=;
     // If we know nothing about this layer we consider it as frequent as it might be the start
     // of an animation.
     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
         return true;
     }
+    return getFps(now) >= kMinFpsForFrequentLayer;
+}
 
+Fps LayerInfo::getFps(nsecs_t now) const {
     // Find the first active frame
     auto it = mFrameTimes.begin();
     for (; it != mFrameTimes.end(); ++it) {
@@ -91,14 +95,12 @@
 
     const auto numFrames = std::distance(it, mFrameTimes.end());
     if (numFrames < kFrequentLayerWindowSize) {
-        return false;
+        return Fps();
     }
 
-    using fps_approx_ops::operator>=;
-
     // Layer is considered frequent if the average frame rate is higher than the threshold
     const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
-    return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)) >= kMinFpsForFrequentLayer;
+    return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
 }
 
 bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -236,7 +238,7 @@
     if (!isFrequent(now)) {
         ALOGV("%s is infrequent", mName.c_str());
         mLastRefreshRate.animatingOrInfrequent = true;
-        // Infrequent layers vote for minimal refresh rate for
+        // Infrequent layers vote for mininal refresh rate for
         // battery saving purposes and also to prevent b/135718869.
         return {LayerHistory::LayerVoteType::Min, Fps()};
     }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 690abda..2d88a4f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -178,6 +178,9 @@
     // Returns a C string for tracing a vote
     const char* getTraceTag(LayerHistory::LayerVoteType type) const;
 
+    // Return the framerate of this layer.
+    Fps getFps(nsecs_t now) const;
+
     void onLayerInactive(nsecs_t now) {
         // Mark mFrameTimeValidSince to now to ignore all previous frame times.
         // We are not deleting the old frame to keep track of whether we should treat the first
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index e127ff7..818f1ed 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -233,6 +233,11 @@
         return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
     }
 
+    // Returns the framerate of the layer with the given sequence ID
+    float getLayerFramerate(nsecs_t now, int32_t id) const {
+        return mLayerHistory.getLayerFramerate(now, id);
+    }
+
 private:
     friend class TestableScheduler;