Use tripple buffering for compose target am: 3aa5fc5cf6

Original change: https://googleplex-android-review.googlesource.com/c/device/generic/goldfish-opengl/+/24029531

Change-Id: Ic916c84508d9c151e8e38ce2bd860748e913a0f5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/system/hwc3/Android.mk b/system/hwc3/Android.mk
index 65d3310..fad6cd8 100644
--- a/system/hwc3/Android.mk
+++ b/system/hwc3/Android.mk
@@ -83,6 +83,7 @@
     DisplayConfig.cpp \
     DisplayFinder.cpp \
     Drm.cpp \
+    DrmSwapchain.cpp \
     DrmAtomicRequest.cpp \
     DrmBuffer.cpp \
     DrmClient.cpp \
diff --git a/system/hwc3/DrmSwapchain.cpp b/system/hwc3/DrmSwapchain.cpp
new file mode 100644
index 0000000..ed69225
--- /dev/null
+++ b/system/hwc3/DrmSwapchain.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2023 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 "DrmSwapchain.h"
+
+#include <log/log.h>
+#include <sync/sync.h>
+#include <ui/GraphicBufferAllocator.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+DrmSwapchain::Image::Image(const native_handle_t* buffer, std::shared_ptr<DrmBuffer> drmBuffer)
+    : mBuffer(buffer), mDrmBuffer(drmBuffer) {}
+
+DrmSwapchain::Image::Image(Image&& other)
+    : mBuffer(std::move(other.mBuffer)),
+      mDrmBuffer(std::move(other.mDrmBuffer)),
+      mLastUseFenceFd(std::move(other.mLastUseFenceFd)) {
+    other.mBuffer = 0;
+}
+
+DrmSwapchain::Image::~Image() { ::android::GraphicBufferAllocator::get().free(mBuffer); }
+
+int DrmSwapchain::Image::wait() {
+    if (!mLastUseFenceFd.ok()) {
+        return 0;
+    }
+    int err = sync_wait(mLastUseFenceFd.get(), 3000);
+    mLastUseFenceFd = ::android::base::unique_fd();
+    if (err < 0 && errno == ETIME) {
+        ALOGE("%s waited on fence %d for 3000 ms", __FUNCTION__, mLastUseFenceFd.get());
+    }
+    if (err < 0) {
+        return err;
+    }
+    return 0;
+}
+
+void DrmSwapchain::Image::markAsInUse(::android::base::unique_fd useCompleteFenceFd) {
+    mLastUseFenceFd = std::move(useCompleteFenceFd);
+}
+
+const native_handle_t* DrmSwapchain::Image::getBuffer() { return mBuffer; }
+
+const std::shared_ptr<DrmBuffer> DrmSwapchain::Image::getDrmBuffer() { return mDrmBuffer; }
+
+std::unique_ptr<DrmSwapchain> DrmSwapchain::create(uint32_t width, uint32_t height, uint32_t usage,
+                                                   DrmClient* client, uint32_t numImages) {
+    std::vector<Image> images;
+    for (uint32_t i = 0; i < numImages; i++) {
+        const uint32_t layerCount = 1;
+        buffer_handle_t handle;
+        uint32_t stride;
+        if (::android::GraphicBufferAllocator::get().allocate(
+                width, height, ::android::PIXEL_FORMAT_RGBA_8888, layerCount, usage, &handle,
+                &stride, "RanchuHwc") != ::android::OK) {
+            return nullptr;
+        }
+        auto ahb = static_cast<const native_handle_t*>(handle);
+
+        HWC3::Error drmBufferCreateError;
+        std::shared_ptr<DrmBuffer> drmBuffer;
+        if (client) {
+            std::tie(drmBufferCreateError, drmBuffer) = client->create(ahb);
+            if (drmBufferCreateError != HWC3::Error::None) {
+                ALOGE("%s: failed to create target drm ahb", __FUNCTION__);
+                return nullptr;
+            }
+        }
+
+        images.emplace_back(Image(ahb, std::move(drmBuffer)));
+    }
+    return std::unique_ptr<DrmSwapchain>(new DrmSwapchain(std::move(images)));
+}
+
+DrmSwapchain::DrmSwapchain(std::vector<Image> images) : mImages(std::move(images)) {}
+
+DrmSwapchain::Image* DrmSwapchain::getNextImage() {
+    auto index = (mLastUsedIndex + 1) % mImages.size();
+    mLastUsedIndex = index;
+    return &mImages[index];
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/DrmSwapchain.h b/system/hwc3/DrmSwapchain.h
new file mode 100644
index 0000000..428763e
--- /dev/null
+++ b/system/hwc3/DrmSwapchain.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+#ifndef ANDROID_HWC_DRMSWAPCHAIN_H
+#define ANDROID_HWC_DRMSWAPCHAIN_H
+
+#include <android-base/unique_fd.h>
+
+#include "Common.h"
+#include "DrmClient.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class DrmSwapchain {
+   public:
+    class Image {
+       public:
+        Image() = delete;
+        ~Image();
+        int wait();
+        void markAsInUse(::android::base::unique_fd useCompleteFenceFd);
+        const native_handle_t* getBuffer();
+        const std::shared_ptr<DrmBuffer> getDrmBuffer();
+        Image(Image&& other);
+
+       private:
+        Image(const native_handle_t*, std::shared_ptr<DrmBuffer>);
+        const native_handle_t* mBuffer = nullptr;
+        std::shared_ptr<DrmBuffer> mDrmBuffer;
+        ::android::base::unique_fd mLastUseFenceFd;
+
+        friend class DrmSwapchain;
+    };
+
+    static std::unique_ptr<DrmSwapchain> create(uint32_t width, uint32_t height, uint32_t usage,
+                                                DrmClient* client, uint32_t numImages = 3);
+    Image* getNextImage();
+
+   private:
+    DrmSwapchain(std::vector<Image> images);
+    std::vector<Image> mImages;
+    uint32_t mLastUsedIndex = 0;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/GuestFrameComposer.cpp b/system/hwc3/GuestFrameComposer.cpp
index b4eef6a..832ded9 100644
--- a/system/hwc3/GuestFrameComposer.cpp
+++ b/system/hwc3/GuestFrameComposer.cpp
@@ -489,46 +489,24 @@
   }
 
   DisplayInfo& displayInfo = mDisplayInfos[displayId];
-
-  uint32_t bufferStride;
-  buffer_handle_t bufferHandle;
-
-  auto status = ::android::GraphicBufferAllocator::get().allocate(
-      displayWidth,                       //
-      displayHeight,                      //
-      ::android::PIXEL_FORMAT_RGBA_8888,  //
-      /*layerCount=*/1,                   //
-      ::android::GraphicBuffer::USAGE_HW_COMPOSER |
-          ::android::GraphicBuffer::USAGE_SW_READ_OFTEN |
-          ::android::GraphicBuffer::USAGE_SW_WRITE_OFTEN,  //
-      &bufferHandle,                                       //
-      &bufferStride,                                       //
-      "RanchuHwc");
-  if (status != ::android::OK) {
-    ALOGE("%s: failed to allocate composition buffer for display:%" PRIu64,
-          __FUNCTION__, displayId);
-    return HWC3::Error::NoResources;
-  }
-
-  displayInfo.compositionResultBuffer = bufferHandle;
-
-  auto [drmBufferCreateError, drmBuffer] = mDrmClient.create(bufferHandle);
-  if (drmBufferCreateError != HWC3::Error::None) {
-    ALOGE("%s: failed to create drm buffer for display:%" PRIu64, __FUNCTION__,
-          displayId);
-    return drmBufferCreateError;
-  }
-  displayInfo.compositionResultDrmBuffer = std::move(drmBuffer);
+  displayInfo.swapchain = DrmSwapchain::create(displayWidth, displayHeight,
+                                               ::android::GraphicBuffer::USAGE_HW_COMPOSER |
+                                                   ::android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                                   ::android::GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+                                               &mDrmClient);
 
   if (displayId == 0) {
-    auto [flushError, flushSyncFd] = mDrmClient.flushToDisplay(
-        displayId, displayInfo.compositionResultDrmBuffer, -1);
+    auto compositionResult = displayInfo.swapchain->getNextImage();
+    auto [flushError, flushSyncFd] =
+        mDrmClient.flushToDisplay(displayId, compositionResult->getDrmBuffer(), -1);
     if (flushError != HWC3::Error::None) {
       ALOGW(
           "%s: Initial display flush failed. HWComposer assuming that we are "
           "running in QEMU without a display and disabling presenting.",
           __FUNCTION__);
       mPresentDisabled = true;
+    } else {
+      compositionResult->markAsInUse(std::move(flushSyncFd));
     }
   }
 
@@ -549,14 +527,7 @@
           displayId);
     return HWC3::Error::BadDisplay;
   }
-
-  DisplayInfo& displayInfo = mDisplayInfos[displayId];
-
-  ::android::GraphicBufferAllocator::get().free(
-      displayInfo.compositionResultBuffer);
-
-  mDisplayInfos.erase(it);
-
+  mDisplayInfos.erase(displayId);
   return HWC3::Error::None;
 }
 
@@ -727,21 +698,23 @@
   }
 
   DisplayInfo& displayInfo = it->second;
+  auto compositionResult = displayInfo.swapchain->getNextImage();
+  compositionResult->wait();
 
-  if (displayInfo.compositionResultBuffer == nullptr) {
+  if (compositionResult->getBuffer() == nullptr) {
     ALOGE("%s: display:%" PRIu64 " missing composition result buffer",
           __FUNCTION__, displayId);
     return HWC3::Error::NoResources;
   }
 
-  if (displayInfo.compositionResultDrmBuffer == nullptr) {
+  if (compositionResult->getDrmBuffer() == nullptr) {
     ALOGE("%s: display:%" PRIu64 " missing composition result drm buffer",
           __FUNCTION__, displayId);
     return HWC3::Error::NoResources;
   }
 
   std::optional<GrallocBuffer> compositionResultBufferOpt =
-      mGralloc.Import(displayInfo.compositionResultBuffer);
+      mGralloc.Import(compositionResult->getBuffer());
   if (!compositionResultBufferOpt) {
     ALOGE("%s: display:%" PRIu64 " failed to import buffer", __FUNCTION__,
           displayId);
@@ -886,14 +859,16 @@
   DEBUG_LOG("%s display:%" PRIu64 " flushing drm buffer", __FUNCTION__,
             displayId);
 
-  auto [error, fence] = mDrmClient.flushToDisplay(
-      displayId, displayInfo.compositionResultDrmBuffer, -1);
+  auto [error, fence] = mDrmClient.flushToDisplay(displayId, compositionResult->getDrmBuffer(), -1);
   if (error != HWC3::Error::None) {
     ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64,
           __FUNCTION__, displayId);
   }
 
   *outDisplayFence = std::move(fence);
+  compositionResult->markAsInUse(outDisplayFence->ok()
+                                     ? ::android::base::unique_fd(dup(*outDisplayFence))
+                                     : ::android::base::unique_fd());
   return error;
 }
 
diff --git a/system/hwc3/GuestFrameComposer.h b/system/hwc3/GuestFrameComposer.h
index 2df2f32..2119735 100644
--- a/system/hwc3/GuestFrameComposer.h
+++ b/system/hwc3/GuestFrameComposer.h
@@ -20,6 +20,7 @@
 #include "Common.h"
 #include "Display.h"
 #include "DrmClient.h"
+#include "DrmSwapchain.h"
 #include "FrameComposer.h"
 #include "Gralloc.h"
 #include "Layer.h"
@@ -92,10 +93,8 @@
                                std::uint32_t dstBufferBytesPerPixel);
 
   struct DisplayInfo {
-    // Additional per display buffer for the composition result.
-    buffer_handle_t compositionResultBuffer = nullptr;
-
-    std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
+    // Additional per display buffers for the composition result.
+    std::unique_ptr<DrmSwapchain> swapchain = {};
   };
 
   std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/HostFrameComposer.cpp b/system/hwc3/HostFrameComposer.cpp
index 044c081..6264052 100644
--- a/system/hwc3/HostFrameComposer.cpp
+++ b/system/hwc3/HostFrameComposer.cpp
@@ -176,23 +176,6 @@
   ComposeDevice_v2* mComposeDevice;
 };
 
-const native_handle_t* AllocateDisplayColorBuffer(int width, int height) {
-  const uint32_t layerCount = 1;
-  const uint64_t graphicBufferId = 0;  // not used
-  buffer_handle_t handle;
-  uint32_t stride;
-
-  if (::android::GraphicBufferAllocator::get().allocate(
-          width, height, ::android::PIXEL_FORMAT_RGBA_8888, layerCount,
-          ::android::GraphicBuffer::USAGE_HW_COMPOSER |
-              ::android::GraphicBuffer::USAGE_HW_RENDER,
-          &handle, &stride, graphicBufferId, "EmuHWC2") == ::android::OK) {
-    return static_cast<const native_handle_t*>(handle);
-  } else {
-    return nullptr;
-  }
-}
-
 void FreeDisplayColorBuffer(const native_handle_t* h) {
   ::android::GraphicBufferAllocator::get().free(h);
 }
@@ -268,29 +251,14 @@
   HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
 
   displayInfo.hostDisplayId = hostDisplayId;
-
-  if (displayInfo.compositionResultBuffer) {
-    FreeDisplayColorBuffer(displayInfo.compositionResultBuffer);
-  }
-  displayInfo.compositionResultBuffer =
-      AllocateDisplayColorBuffer(displayWidth, displayHeight);
-  if (displayInfo.compositionResultBuffer == nullptr) {
-    ALOGE("%s: display:%" PRIu64 " failed to create target buffer",
-          __FUNCTION__, displayId);
+  displayInfo.swapchain = DrmSwapchain::create(
+      displayWidth, displayHeight,
+      ::android::GraphicBuffer::USAGE_HW_COMPOSER | ::android::GraphicBuffer::USAGE_HW_RENDER,
+      mDrmClient ? &mDrmClient.value() : nullptr);
+  if (!displayInfo.swapchain) {
+    ALOGE("%s: display:%" PRIu64 " failed to allocate swapchain", __FUNCTION__, displayId);
     return HWC3::Error::NoResources;
   }
-
-  if (mDrmClient) {
-    auto [drmBufferCreateError, drmBuffer] =
-        mDrmClient->create(displayInfo.compositionResultBuffer);
-    if (drmBufferCreateError != HWC3::Error::None) {
-      ALOGE("%s: display:%" PRIu64 " failed to create target drm buffer",
-            __FUNCTION__, displayId);
-      return HWC3::Error::NoResources;
-    }
-    displayInfo.compositionResultDrmBuffer = std::move(drmBuffer);
-  }
-
   return HWC3::Error::None;
 }
 
@@ -413,8 +381,6 @@
     hostCon->unlock();
   }
 
-  FreeDisplayColorBuffer(displayInfo.compositionResultBuffer);
-
   mDisplayInfos.erase(it);
 
   return HWC3::Error::None;
@@ -554,6 +520,7 @@
   if (error != HWC3::Error::None) {
     return error;
   }
+  *outDisplayFence = ::android::base::unique_fd();
   hostCon->lock();
   bool hostCompositionV1 = rcEnc->hasHostCompositionV1();
   bool hostCompositionV2 = rcEnc->hasHostCompositionV2();
@@ -564,6 +531,9 @@
     hostCompositionV1 = false;
   }
 
+  auto compositionResult = displayInfo.swapchain->getNextImage();
+  compositionResult->wait();
+
   const std::vector<Layer*> layers = display->getOrderedLayers();
   if (hostCompositionV2 || hostCompositionV1) {
     uint32_t numLayer = 0;
@@ -672,16 +642,15 @@
           layer->getZOrder(), l->composeMode, l->transform);
       l++;
     }
+
     if (hostCompositionV1) {
       p->version = 1;
-      p->targetHandle = hostCon->grallocHelper()->getHostHandle(
-          displayInfo.compositionResultBuffer);
+      p->targetHandle = hostCon->grallocHelper()->getHostHandle(compositionResult->getBuffer());
       p->numLayers = numLayer;
     } else {
       p2->version = 2;
       p2->displayId = displayInfo.hostDisplayId;
-      p2->targetHandle = hostCon->grallocHelper()->getHostHandle(
-          displayInfo.compositionResultBuffer);
+      p2->targetHandle = hostCon->grallocHelper()->getHostHandle(compositionResult->getBuffer());
       p2->numLayers = numLayer;
     }
 
@@ -732,8 +701,8 @@
     }
 
     if (mIsMinigbm) {
-      auto [_, fence] = mDrmClient->flushToDisplay(
-          displayId, displayInfo.compositionResultDrmBuffer, -1);
+      auto [_, fence] =
+          mDrmClient->flushToDisplay(displayId, compositionResult->getDrmBuffer(), -1);
       retire_fd = std::move(fence);
     } else {
       int fd;
@@ -756,7 +725,6 @@
       }
       hostCon->unlock();
     }
-
   } else {
     // we set all layers Composition::CLIENT, so do nothing.
     FencedBuffer& displayClientTarget = display->getClientTarget();
@@ -764,8 +732,7 @@
         displayClientTarget.getFence();
     if (mIsMinigbm) {
       auto [_, flushFence] = mDrmClient->flushToDisplay(
-          displayId, displayInfo.compositionResultDrmBuffer,
-          displayClientTargetFence);
+          displayId, compositionResult->getDrmBuffer(), displayClientTargetFence);
       *outDisplayFence = std::move(flushFence);
     } else {
           rcEnc->rcSetDisplayColorBuffer(rcEnc, displayInfo.hostDisplayId,
@@ -776,6 +743,9 @@
     ALOGV("%s fallback to post, returns outRetireFence %d", __FUNCTION__,
           outDisplayFence->get());
   }
+  compositionResult->markAsInUse(outDisplayFence->ok()
+                                     ? ::android::base::unique_fd(dup(*outDisplayFence))
+                                     : ::android::base::unique_fd());
   return HWC3::Error::None;
 }
 
diff --git a/system/hwc3/HostFrameComposer.h b/system/hwc3/HostFrameComposer.h
index aebf716..b42f480 100644
--- a/system/hwc3/HostFrameComposer.h
+++ b/system/hwc3/HostFrameComposer.h
@@ -24,6 +24,7 @@
 
 #include "Common.h"
 #include "DrmClient.h"
+#include "DrmSwapchain.h"
 #include "FrameComposer.h"
 #include "HostConnection.h"
 
@@ -87,13 +88,7 @@
 
   struct HostComposerDisplayInfo {
     uint32_t hostDisplayId = 0;
-
-    // Additional per display buffer for the composition result.
-    const native_handle_t* compositionResultBuffer = nullptr;
-
-    // Drm info for the additional composition result buffer.
-    std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
-
+    std::unique_ptr<DrmSwapchain> swapchain = {};
     // Drm info for the displays client target buffer.
     std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
   };