Fix flickering with client frame composer

... by caching the DRM buffers.

Bug: b/239855014
Test: manual run
Change-Id: I212114e37f0ab7c2b678b29e67841715462a7523
diff --git a/system/hwc3/ClientFrameComposer.h b/system/hwc3/ClientFrameComposer.h
index 6961354..28199b3 100644
--- a/system/hwc3/ClientFrameComposer.h
+++ b/system/hwc3/ClientFrameComposer.h
@@ -70,7 +70,7 @@
 
  private:
   struct DisplayInfo {
-    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;;
+    std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
   };
 
   std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/DrmPresenter.cpp b/system/hwc3/DrmPresenter.cpp
index a62e17b..b512277 100644
--- a/system/hwc3/DrmPresenter.cpp
+++ b/system/hwc3/DrmPresenter.cpp
@@ -80,6 +80,18 @@
       ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
       return HWC3::Error::NoResources;
     }
+
+    constexpr const std::size_t kCachedBuffersPerDisplay = 3;
+    std::size_t numDisplays = 0;
+    for (const DrmConnector& connector : mConnectors) {
+      if (connector.connection == DRM_MODE_CONNECTED) {
+        ++numDisplays;
+      }
+    }
+    const std::size_t bufferCacheSize = kCachedBuffersPerDisplay * numDisplays;
+    DEBUG_LOG("%s: initializing DRM buffer cache to size %zu",
+              __FUNCTION__, bufferCacheSize);
+    mBufferCache = std::make_unique<DrmBufferCache>(bufferCacheSize);
   }
 
   mDrmEventListener = ::android::sp<DrmEventListener>::make(*this);
@@ -347,54 +359,59 @@
   mPlanes.clear();
 }
 
-std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> DrmPresenter::create(
+std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> DrmPresenter::create(
     const native_handle_t* handle) {
-  auto buffer = std::unique_ptr<DrmBuffer>(new DrmBuffer(*this));
-
   cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
   if (crosHandle == nullptr) {
     ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
-    return std::make_tuple(HWC3::Error::NoResources,
-                           std::unique_ptr<DrmBuffer>());
+    return std::make_tuple(HWC3::Error::NoResources, nullptr);
   }
 
+  DrmPrimeBufferHandle primeHandle = 0;
+  int ret = drmPrimeFDToHandle(mFd.get(), crosHandle->fds[0], &primeHandle);
+  if (ret) {
+    ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
+          strerror(errno), errno);
+    return std::make_tuple(HWC3::Error::NoResources, nullptr);
+  }
+
+  auto drmBufferPtr = mBufferCache->get(primeHandle);
+  if (drmBufferPtr != nullptr) {
+    return std::make_tuple(HWC3::Error::None,
+                           std::shared_ptr<DrmBuffer>(*drmBufferPtr));
+  }
+
+  auto buffer = std::shared_ptr<DrmBuffer>(new DrmBuffer(*this));
   buffer->mWidth = crosHandle->width;
   buffer->mHeight = crosHandle->height;
   buffer->mDrmFormat = crosHandle->format;
   buffer->mPlaneFds[0] = crosHandle->fds[0];
+  buffer->mPlaneHandles[0] = primeHandle;
   buffer->mPlanePitches[0] = crosHandle->strides[0];
   buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
 
-  HWC3::Error error = createDrmFramebuffer(buffer.get());
-  return std::make_tuple(error, std::move(buffer));
-}
-
-HWC3::Error DrmPresenter::createDrmFramebuffer(DrmBuffer* buffer) {
-  int ret;
-
-  ret = drmPrimeFDToHandle(mFd.get(), buffer->mPlaneFds[0],
-                           &buffer->mPlaneHandles[0]);
-  if (ret) {
-    ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
-          strerror(errno), errno);
-    return HWC3::Error::NoResources;
-  }
-
   uint32_t framebuffer = 0;
-  ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight,
-                      buffer->mDrmFormat, buffer->mPlaneHandles,
-                      buffer->mPlanePitches, buffer->mPlaneOffsets,
-                      &framebuffer, 0);
+  ret = drmModeAddFB2(mFd.get(),
+                      buffer->mWidth,
+                      buffer->mHeight,
+                      buffer->mDrmFormat,
+                      buffer->mPlaneHandles,
+                      buffer->mPlanePitches,
+                      buffer->mPlaneOffsets,
+                      &framebuffer,
+                      0);
   if (ret) {
     ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__,
           strerror(errno), errno);
-    return HWC3::Error::NoResources;
+    return std::make_tuple(HWC3::Error::NoResources, nullptr);
   }
-
   DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+  buffer->mDrmFramebuffer = framebuffer;
 
-  buffer->mDrmFramebuffer.emplace(framebuffer);
-  return HWC3::Error::None;
+  mBufferCache->set(primeHandle, std::shared_ptr<DrmBuffer>(buffer));
+
+  return std::make_tuple(HWC3::Error::None,
+                         std::shared_ptr<DrmBuffer>(buffer));
 }
 
 HWC3::Error DrmPresenter::destroyDrmFramebuffer(DrmBuffer* buffer) {
@@ -416,6 +433,8 @@
             strerror(errno), errno);
       return HWC3::Error::NoResources;
     }
+
+    mBufferCache->remove(buffer->mPlaneHandles[0]);
   }
 
   return HWC3::Error::None;
diff --git a/system/hwc3/DrmPresenter.h b/system/hwc3/DrmPresenter.h
index c87e5aa..72e3f5a 100644
--- a/system/hwc3/DrmPresenter.h
+++ b/system/hwc3/DrmPresenter.h
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include "Common.h"
+#include "LruCache.h"
 #include "android/base/synchronization/AndroidLock.h"
 
 namespace aidl::android::hardware::graphics::composer3::impl {
@@ -60,7 +61,6 @@
   uint32_t mPlaneHandles[4] = {0, 0, 0, 0};
   uint32_t mPlanePitches[4] = {0, 0, 0, 0};
   uint32_t mPlaneOffsets[4] = {0, 0, 0, 0};
-
   std::optional<uint32_t> mDrmFramebuffer;
 };
 
@@ -101,7 +101,7 @@
 
   uint32_t refreshRate() const { return mConnectors[0].mRefreshRateAsInteger; }
 
-  std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> create(
+  std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> create(
       const native_handle_t* handle);
 
   std::tuple<HWC3::Error, ::android::base::unique_fd> flushToDisplay(
@@ -111,9 +111,13 @@
   std::optional<std::vector<uint8_t>> getEdid(uint32_t id);
 
  private:
-  // Grant visibility for createDrmFramebuffer and clearDrmFB to DrmBuffer.
+  // TODO: make this cache per display when enabling hotplug support.
+  using DrmPrimeBufferHandle = uint32_t;
+  using DrmBufferCache = LruCache<DrmPrimeBufferHandle, std::shared_ptr<DrmBuffer>>;
+  std::unique_ptr<DrmBufferCache> mBufferCache;
+
+  // Grant visibility to destroyDrmFramebuffer to DrmBuffer.
   friend class DrmBuffer;
-  HWC3::Error createDrmFramebuffer(DrmBuffer* buffer);
   HWC3::Error destroyDrmFramebuffer(DrmBuffer* buffer);
 
   // Grant visibility for handleHotplug to DrmEventListener.
diff --git a/system/hwc3/GuestFrameComposer.h b/system/hwc3/GuestFrameComposer.h
index 2ab1a82..e37ff87 100644
--- a/system/hwc3/GuestFrameComposer.h
+++ b/system/hwc3/GuestFrameComposer.h
@@ -95,7 +95,7 @@
     // Additional per display buffer for the composition result.
     buffer_handle_t compositionResultBuffer = nullptr;
 
-    std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+    std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
   };
 
   std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/HostFrameComposer.h b/system/hwc3/HostFrameComposer.h
index b2b042c..42eb43a 100644
--- a/system/hwc3/HostFrameComposer.h
+++ b/system/hwc3/HostFrameComposer.h
@@ -92,10 +92,10 @@
     const native_handle_t* compositionResultBuffer = nullptr;
 
     // Drm info for the additional composition result buffer.
-    std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+    std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
 
     // Drm info for the displays client target buffer.
-    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;
+    std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
   };
 
   std::unordered_map<int64_t, HostComposerDisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/LruCache.h b/system/hwc3/LruCache.h
new file mode 100644
index 0000000..9ffca46
--- /dev/null
+++ b/system/hwc3/LruCache.h
@@ -0,0 +1,83 @@
+// Copyright 2022 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 <list>
+#include <unordered_map>
+
+template <typename Key, typename Value>
+class LruCache {
+  public:
+    LruCache(std::size_t maxSize) : m_maxSize(maxSize) {
+        m_table.reserve(maxSize);
+    }
+
+    Value* get(const Key& key) {
+        auto tableIt = m_table.find(key);
+        if (tableIt == m_table.end()) {
+            return nullptr;
+        }
+
+        // Move to front.
+        auto elementsIt = tableIt->second;
+        m_elements.splice(elementsIt, m_elements, m_elements.begin());
+        return &elementsIt->value;
+    }
+
+    void set(const Key& key, Value&& value) {
+        auto tableIt = m_table.find(key);
+        if (tableIt == m_table.end()) {
+            if (m_table.size() >= m_maxSize) {
+                auto& kv = m_elements.back();
+                m_table.erase(kv.key);
+                m_elements.pop_back();
+            }
+        } else {
+            auto elementsIt = tableIt->second;
+            m_elements.erase(elementsIt);
+        }
+        m_elements.emplace_front(KeyValue{
+            key,
+            std::forward<Value>(value),
+        });
+        m_table[key] = m_elements.begin();
+    }
+
+    void remove(const Key& key) {
+        auto tableIt = m_table.find(key);
+        if (tableIt == m_table.end()) {
+            return;
+        }
+        auto elementsIt = tableIt->second;
+        m_elements.erase(elementsIt);
+        m_table.erase(tableIt);
+    }
+
+    void clear() {
+        m_elements.clear();
+        m_table.clear();
+    }
+
+  private:
+    struct KeyValue {
+        Key key;
+        Value value;
+    };
+
+    const std::size_t m_maxSize;
+    // Front is the most recently used and back is the least recently used.
+    std::list<KeyValue> m_elements;
+    std::unordered_map<Key, typename std::list<KeyValue>::iterator> m_table;
+};