drm_hwcomposer: Merge 'aosp/upstream-main' into HEAD am: cd31422280 am: 5944fd14ed am: 9a34e46f1b am: 48181a034b am: c98eee1cd4

Original change: https://android-review.googlesource.com/c/platform/external/drm_hwcomposer/+/2143433

Change-Id: I0af938325727d7221e08fdc26455e41920d6f4ad
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.ci/.gitlab-ci-checkcommit.sh b/.ci/.gitlab-ci-checkcommit.sh
index c9c2e49..c6f7c4e 100755
--- a/.ci/.gitlab-ci-checkcommit.sh
+++ b/.ci/.gitlab-ci-checkcommit.sh
@@ -50,7 +50,7 @@
 		exit 1
 	fi
 
-	git show "$h" -- | clang-format-diff-13 -p 1 -style=file > /tmp/format-fixup.patch
+	git show "$h" -- | clang-format-diff-14 -p 1 -style=file > /tmp/format-fixup.patch
 	if [ -s  /tmp/format-fixup.patch ]; then
 		cat /tmp/format-fixup.patch >&2
 		exit 1
diff --git a/.ci/Makefile b/.ci/Makefile
index a0e4b73..581c6d9 100644
--- a/.ci/Makefile
+++ b/.ci/Makefile
@@ -2,8 +2,8 @@
 INCLUDE_DIRS := . ../libdrm/include/drm include ./.ci/android_headers ./tests/test_include
 SYSTEM_INCLUDE_DIRS := /usr/include/libdrm
 
-CLANG := clang++-13
-CLANG_TIDY := clang-tidy-13
+CLANG := clang++-14
+CLANG_TIDY := clang-tidy-14
 OUT_DIR := /tmp/drm_hwcomposer/build
 SRC_DIR := .
 
@@ -43,6 +43,7 @@
     -cppcoreguidelines-pro-type-vararg                  \
     -hicpp-vararg                                       \
     -hicpp-signed-bitwise                               \
+    -readability-identifier-length                      \
 
 TIDY_CHECKS_NORMAL :=                                   \
     $(TIDY_CHECKS_FINE)                                 \
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 89ac1b8..eb0c265 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,11 +1,11 @@
-image: ubuntu:21.10
+image: ubuntu:22.04
 
 variables:
   DEBIAN_FRONTEND: noninteractive
 
 before_script:
   - apt-get --quiet update --yes >/dev/null
-  - apt-get --quiet install --yes clang-13 clang-tidy-13 clang-format-13 git libdrm-dev blueprint-tools libgtest-dev make >/dev/null
+  - apt-get --quiet install --yes clang-14 clang-tidy-14 clang-format-14 git libdrm-dev blueprint-tools libgtest-dev make >/dev/null
 
 stages:
   - build
diff --git a/Android.bp b/Android.bp
index c9c94af..a7a2e94 100644
--- a/Android.bp
+++ b/Android.bp
@@ -37,10 +37,7 @@
 
     srcs: ["utils/Worker.cpp"],
 
-    include_dirs: [
-        "external/drm_hwcomposer",
-        "external/drm_hwcomposer/include",
-    ],
+    include_dirs: ["external/drm_hwcomposer"],
 
     cflags: [
         "-Wall",
@@ -68,10 +65,7 @@
         "libutils",
     ],
 
-    include_dirs: [
-        "external/drm_hwcomposer",
-        "external/drm_hwcomposer/include",
-    ],
+    include_dirs: ["external/drm_hwcomposer"],
 
     static_libs: ["libdrmhwc_utils"],
 
@@ -118,8 +112,6 @@
         "drm/UEventListener.cpp",
         "drm/VSyncWorker.cpp",
 
-        "utils/hwcutils.cpp",
-
         "backend/Backend.cpp",
         "backend/BackendClient.cpp",
         "backend/BackendManager.cpp",
diff --git a/README.md b/README.md
index 05ddc79..e98b5ae 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
   you with formatting of your patches:
 
     ```
-    git diff | clang-format-diff-13 -p 1 -style=file
+    git diff | clang-format-diff-14 -p 1 -style=file
     ```
 
 * Hardware specific changes should be tested on relevant platforms before
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index f6d9c18..ba0518a 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -83,9 +83,9 @@
 
 bool Backend::IsClientLayer(HwcDisplay *display, HwcLayer *layer) {
   return !HardwareSupportsLayerType(layer->GetSfType()) ||
-         !BufferInfoGetter::GetInstance()->IsHandleUsable(layer->GetBuffer()) ||
+         !layer->IsLayerUsableAsDevice() ||
          display->color_transform_hint() != HAL_COLOR_TRANSFORM_IDENTITY ||
-         (layer->RequireScalingOrPhasing() &&
+         (layer->GetLayerData().pi.RequireScalingOrPhasing() &&
           display->GetHwc2()->GetResMan().ForcedScalingWithGpu());
 }
 
@@ -99,7 +99,7 @@
   uint32_t pixops = 0;
   for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
     if (z_order >= first_z && z_order < first_z + size) {
-      hwc_rect_t df = layers[z_order]->GetDisplayFrame();
+      hwc_rect_t &df = layers[z_order]->GetLayerData().pi.display_frame;
       pixops += (df.right - df.left) * (df.bottom - df.top);
     }
   }
diff --git a/bufferinfo/BufferInfo.h b/bufferinfo/BufferInfo.h
new file mode 100644
index 0000000..b2297f9
--- /dev/null
+++ b/bufferinfo/BufferInfo.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 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 <cstdint>
+
+constexpr int kBufferMaxPlanes = 4;
+
+enum class BufferColorSpace : int32_t {
+  kUndefined,
+  kItuRec601,
+  kItuRec709,
+  kItuRec2020,
+};
+
+enum class BufferSampleRange : int32_t {
+  kUndefined,
+  kFullRange,
+  kLimitedRange,
+};
+
+enum class BufferBlendMode : int32_t {
+  kUndefined,
+  kNone,
+  kPreMult,
+  kCoverage,
+};
+
+struct BufferInfo {
+  uint32_t width;
+  uint32_t height;
+  uint32_t format; /* DRM_FORMAT_* from drm_fourcc.h */
+  uint32_t pitches[kBufferMaxPlanes];
+  uint32_t offsets[kBufferMaxPlanes];
+  /* sizes[] is used only by mapper@4 metadata getter for internal purposes */
+  uint32_t sizes[kBufferMaxPlanes];
+  int prime_fds[kBufferMaxPlanes];
+  uint64_t modifiers[kBufferMaxPlanes];
+
+  BufferColorSpace color_space;
+  BufferSampleRange sample_range;
+  BufferBlendMode blend_mode;
+};
diff --git a/bufferinfo/BufferInfoGetter.cpp b/bufferinfo/BufferInfoGetter.cpp
index 0cfd7e5..95c1a23 100644
--- a/bufferinfo/BufferInfoGetter.cpp
+++ b/bufferinfo/BufferInfoGetter.cpp
@@ -22,9 +22,14 @@
 #include "BufferInfoMapperMetadata.h"
 #endif
 
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
+#include <mutex>
+
 #include "utils/log.h"
 #include "utils/properties.h"
 
@@ -48,17 +53,18 @@
   return inst.get();
 }
 
-bool BufferInfoGetter::IsHandleUsable(buffer_handle_t handle) {
-  hwc_drm_bo_t bo;
-  memset(&bo, 0, sizeof(hwc_drm_bo_t));
+std::optional<BufferUniqueId> BufferInfoGetter::GetUniqueId(
+    buffer_handle_t handle) {
+  struct stat sb {};
+  if (fstat(handle->data[0], &sb) != 0) {
+    return {};
+  }
 
-  if (ConvertBoInfo(handle, &bo) != 0) {
-    return false;
+  if (sb.st_size == 0) {
+    return {};
   }
-  if (bo.prime_fds[0] == 0) {
-    return false;
-  }
-  return true;
+
+  return static_cast<BufferUniqueId>(sb.st_ino);
 }
 
 int LegacyBufferInfoGetter::Init() {
diff --git a/bufferinfo/BufferInfoGetter.h b/bufferinfo/BufferInfoGetter.h
index 59184a4..5591296 100644
--- a/bufferinfo/BufferInfoGetter.h
+++ b/bufferinfo/BufferInfoGetter.h
@@ -20,8 +20,10 @@
 #include <drm/drm_fourcc.h>
 #include <hardware/gralloc.h>
 
+#include <optional>
+
+#include "BufferInfo.h"
 #include "drm/DrmDevice.h"
-#include "drmhwcgralloc.h"
 
 #ifndef DRM_FORMAT_INVALID
 #define DRM_FORMAT_INVALID 0
@@ -29,13 +31,16 @@
 
 namespace android {
 
+using BufferUniqueId = uint64_t;
+
 class BufferInfoGetter {
  public:
   virtual ~BufferInfoGetter() = default;
 
-  virtual int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) = 0;
+  virtual auto GetBoInfo(buffer_handle_t handle)
+      -> std::optional<BufferInfo> = 0;
 
-  bool IsHandleUsable(buffer_handle_t handle);
+  virtual std::optional<BufferUniqueId> GetUniqueId(buffer_handle_t handle);
 
   static BufferInfoGetter *GetInstance();
 
diff --git a/bufferinfo/BufferInfoMapperMetadata.cpp b/bufferinfo/BufferInfoMapperMetadata.cpp
index 2f08a76..bdacb74 100644
--- a/bufferinfo/BufferInfoMapperMetadata.cpp
+++ b/bufferinfo/BufferInfoMapperMetadata.cpp
@@ -46,7 +46,7 @@
  * so that it can be overridden.
  */
 int __attribute__((weak))
-BufferInfoMapperMetadata::GetFds(buffer_handle_t handle, hwc_drm_bo_t *bo) {
+BufferInfoMapperMetadata::GetFds(buffer_handle_t handle, BufferInfo *bo) {
   int fd_index = 0;
 
   if (handle->numFds <= 0) {
@@ -54,7 +54,7 @@
     return android::BAD_VALUE;
   }
 
-  for (int i = 0; i < kHwcDrmBoMaxPlanes; i++) {
+  for (int i = 0; i < kBufferMaxPlanes; i++) {
     /* If no size, we're out of usable planes */
     if (bo->sizes[i] <= 0) {
       if (i == 0) {
@@ -86,71 +86,63 @@
   return 0;
 }
 
-int BufferInfoMapperMetadata::ConvertBoInfo(buffer_handle_t handle,
-                                            hwc_drm_bo_t *bo) {
+auto BufferInfoMapperMetadata::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   GraphicBufferMapper &mapper = GraphicBufferMapper::getInstance();
   if (handle == nullptr)
-    return -EINVAL;
+    return {};
 
-  uint64_t usage = 0;
-  int err = mapper.getUsage(handle, &usage);
-  if (err != 0) {
-    ALOGE("Failed to get usage err=%d", err);
-    return err;
-  }
-  bo->usage = static_cast<uint32_t>(usage);
+  BufferInfo bi{};
 
-  ui::PixelFormat hal_format;
-  err = mapper.getPixelFormatRequested(handle, &hal_format);
-  if (err != 0) {
-    ALOGE("Failed to get HAL Pixel Format err=%d", err);
-    return err;
-  }
-  bo->hal_format = static_cast<uint32_t>(hal_format);
-
-  err = mapper.getPixelFormatFourCC(handle, &bo->format);
+  int err = mapper.getPixelFormatFourCC(handle, &bi.format);
   if (err != 0) {
     ALOGE("Failed to get FourCC format err=%d", err);
-    return err;
+    return {};
   }
 
-  err = mapper.getPixelFormatModifier(handle, &bo->modifiers[0]);
+  err = mapper.getPixelFormatModifier(handle, &bi.modifiers[0]);
   if (err != 0) {
     ALOGE("Failed to get DRM Modifier err=%d", err);
-    return err;
+    return {};
   }
 
   uint64_t width = 0;
   err = mapper.getWidth(handle, &width);
   if (err != 0) {
     ALOGE("Failed to get Width err=%d", err);
-    return err;
+    return {};
   }
-  bo->width = static_cast<uint32_t>(width);
+  bi.width = static_cast<uint32_t>(width);
 
   uint64_t height = 0;
   err = mapper.getHeight(handle, &height);
   if (err != 0) {
     ALOGE("Failed to get Height err=%d", err);
-    return err;
+    return {};
   }
-  bo->height = static_cast<uint32_t>(height);
+  bi.height = static_cast<uint32_t>(height);
 
   std::vector<ui::PlaneLayout> layouts;
   err = mapper.getPlaneLayouts(handle, &layouts);
   if (err != 0) {
     ALOGE("Failed to get Plane Layouts err=%d", err);
-    return err;
+    return {};
   }
 
   for (uint32_t i = 0; i < layouts.size(); i++) {
-    bo->modifiers[i] = bo->modifiers[0];
-    bo->pitches[i] = layouts[i].strideInBytes;
-    bo->offsets[i] = layouts[i].offsetInBytes;
-    bo->sizes[i] = layouts[i].totalSizeInBytes;
+    bi.modifiers[i] = bi.modifiers[0];
+    bi.pitches[i] = layouts[i].strideInBytes;
+    bi.offsets[i] = layouts[i].offsetInBytes;
+    bi.sizes[i] = layouts[i].totalSizeInBytes;
   }
 
-  return GetFds(handle, bo);
+  err = GetFds(handle, &bi);
+  if (err != 0) {
+    ALOGE("Failed to get fds (err=%d)", err);
+    return {};
+  }
+
+  return bi;
 }
 
 }  // namespace android
diff --git a/bufferinfo/BufferInfoMapperMetadata.h b/bufferinfo/BufferInfoMapperMetadata.h
index d335705..ab269dc 100644
--- a/bufferinfo/BufferInfoMapperMetadata.h
+++ b/bufferinfo/BufferInfoMapperMetadata.h
@@ -25,9 +25,9 @@
  public:
   using BufferInfoGetter::BufferInfoGetter;
 
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
 
-  int GetFds(buffer_handle_t handle, hwc_drm_bo_t *bo);
+  int GetFds(buffer_handle_t handle, BufferInfo *bo);
 
   static BufferInfoGetter *CreateInstance();
 };
diff --git a/bufferinfo/legacy/BufferInfoImagination.cpp b/bufferinfo/legacy/BufferInfoImagination.cpp
index 691dd14..1858ddb 100644
--- a/bufferinfo/legacy/BufferInfoImagination.cpp
+++ b/bufferinfo/legacy/BufferInfoImagination.cpp
@@ -29,24 +29,24 @@
 
 LEGACY_BUFFER_INFO_GETTER(BufferInfoImagination);
 
-int BufferInfoImagination::ConvertBoInfo(buffer_handle_t handle,
-                                         hwc_drm_bo_t *bo) {
+auto BufferInfoImagination::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   auto *hnd = (IMG_native_handle_t *)handle;
   if (!hnd)
-    return -EINVAL;
+    return {};
 
   /* Extra bits are responsible for buffer compression and memory layout */
   if (hnd->iFormat & ~0x10f) {
     ALOGV("Special buffer formats are not supported");
-    return -EINVAL;
+    return {};
   }
 
-  bo->width = hnd->iWidth;
-  bo->height = hnd->iHeight;
-  bo->usage = hnd->usage;
-  bo->prime_fds[0] = hnd->fd[0];
-  bo->pitches[0] = ALIGN(hnd->iWidth, HW_ALIGN) * hnd->uiBpp >> 3;
-  bo->hal_format = hnd->iFormat;
+  BufferInfo bi{};
+
+  bi.width = hnd->iWidth;
+  bi.height = hnd->iHeight;
+  bi.prime_fds[0] = hnd->fd[0];
+  bi.pitches[0] = ALIGN(hnd->iWidth, HW_ALIGN) * hnd->uiBpp >> 3;
 
   switch (hnd->iFormat) {
 #ifdef HAL_PIXEL_FORMAT_BGRX_8888
@@ -55,14 +55,14 @@
       break;
 #endif
     default:
-      bo->format = ConvertHalFormatToDrm(hnd->iFormat & 0xf);
-      if (bo->format == DRM_FORMAT_INVALID) {
+      bi.format = ConvertHalFormatToDrm(hnd->iFormat & 0xf);
+      if (bi.format == DRM_FORMAT_INVALID) {
         ALOGV("Cannot convert hal format to drm format %u", hnd->iFormat);
-        return -EINVAL;
+        return {};
       }
   }
 
-  return 0;
+  return bi;
 }
 
 }  // namespace android
diff --git a/bufferinfo/legacy/BufferInfoImagination.h b/bufferinfo/legacy/BufferInfoImagination.h
index 765b279..635e3b5 100644
--- a/bufferinfo/legacy/BufferInfoImagination.h
+++ b/bufferinfo/legacy/BufferInfoImagination.h
@@ -27,7 +27,7 @@
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
 
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
 };
 }  // namespace android
 
diff --git a/bufferinfo/legacy/BufferInfoLibdrm.cpp b/bufferinfo/legacy/BufferInfoLibdrm.cpp
index 6baf6bb..ac71ec0 100644
--- a/bufferinfo/legacy/BufferInfoLibdrm.cpp
+++ b/bufferinfo/legacy/BufferInfoLibdrm.cpp
@@ -91,8 +91,8 @@
   return false;
 }
 
-bool BufferInfoLibdrm::GetYuvPlaneInfo(int num_fds, buffer_handle_t handle,
-                                       hwc_drm_bo_t *bo) {
+bool BufferInfoLibdrm::GetYuvPlaneInfo(uint32_t hal_format, int num_fds,
+                                       buffer_handle_t handle, BufferInfo *bo) {
   struct android_ycbcr ycbcr {};
   enum chroma_order chroma_order {};
   int ret = 0;
@@ -136,12 +136,12 @@
 
   /* .chroma_step is the byte distance between the same chroma channel
    * values of subsequent pixels, assumed to be the same for Cb and Cr. */
-  bo->format = get_fourcc_yuv(bo->hal_format, chroma_order, ycbcr.chroma_step);
+  bo->format = get_fourcc_yuv(hal_format, chroma_order, ycbcr.chroma_step);
   if (bo->format == UINT32_MAX) {
     ALOGW(
         "unsupported YUV format, native = %x, chroma_order = %s, chroma_step = "
         "%d",
-        bo->hal_format, chroma_order == kYCbCr ? "YCbCr" : "YCrCb",
+        hal_format, chroma_order == kYCbCr ? "YCbCr" : "YCrCb",
         (int)ycbcr.chroma_step);
     return false;
   }
@@ -162,14 +162,16 @@
   return true;
 }
 
-int BufferInfoLibdrm::ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) {
+auto BufferInfoLibdrm::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   gralloc_handle_t *gr_handle = gralloc_handle(handle);
   if (!gr_handle)
-    return -EINVAL;
+    return {};
 
-  bo->width = gr_handle->width;
-  bo->height = gr_handle->height;
-  bo->hal_format = gr_handle->format;
+  BufferInfo bi{};
+
+  bi.width = gr_handle->width;
+  bi.height = gr_handle->height;
 
 #if GRALLOC_HANDLE_VERSION < 4
   static std::once_flag once;
@@ -179,35 +181,34 @@
   });
 #endif
 #if GRALLOC_HANDLE_VERSION == 4
-  bo->modifiers[0] = gr_handle->modifier;
+  bi.modifiers[0] = gr_handle->modifier;
 #endif
 
-  bo->usage = gr_handle->usage;
-  bo->prime_fds[0] = gr_handle->prime_fd;
+  bi.prime_fds[0] = gr_handle->prime_fd;
 
   if (is_yuv(gr_handle->format)) {
-    if (!GetYuvPlaneInfo(handle->numFds, handle, bo))
-      return -EINVAL;
+    if (!GetYuvPlaneInfo(gr_handle->format, handle->numFds, handle, &bi))
+      return {};
   } else {
-    bo->pitches[0] = gr_handle->stride;
-    bo->offsets[0] = 0;
+    bi.pitches[0] = gr_handle->stride;
+    bi.offsets[0] = 0;
 
     /* FOSS graphic components (gbm_gralloc, mesa3d) are translating
      * HAL_PIXEL_FORMAT_RGB_565 to DRM_FORMAT_RGB565 without swapping
      * the R and B components. Same must be done here. */
-    switch (bo->hal_format) {
+    switch (gr_handle->format) {
       case HAL_PIXEL_FORMAT_RGB_565:
-        bo->format = DRM_FORMAT_RGB565;
+        bi.format = DRM_FORMAT_RGB565;
         break;
       default:
-        bo->format = ConvertHalFormatToDrm(gr_handle->format);
+        bi.format = ConvertHalFormatToDrm(gr_handle->format);
     }
 
-    if (bo->format == DRM_FORMAT_INVALID)
-      return -EINVAL;
+    if (bi.format == DRM_FORMAT_INVALID)
+      return {};
   }
 
-  return 0;
+  return bi;
 }
 
 constexpr char gbm_gralloc_module_name[] = "GBM Memory Allocator";
diff --git a/bufferinfo/legacy/BufferInfoLibdrm.h b/bufferinfo/legacy/BufferInfoLibdrm.h
index cad8add..7f5b08c 100644
--- a/bufferinfo/legacy/BufferInfoLibdrm.h
+++ b/bufferinfo/legacy/BufferInfoLibdrm.h
@@ -26,11 +26,12 @@
 class BufferInfoLibdrm : public LegacyBufferInfoGetter {
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
   int ValidateGralloc() override;
 
  private:
-  bool GetYuvPlaneInfo(int num_fds, buffer_handle_t handle, hwc_drm_bo_t *bo);
+  bool GetYuvPlaneInfo(uint32_t hal_format, int num_fds, buffer_handle_t handle,
+                       BufferInfo *bo);
 };
 
 }  // namespace android
diff --git a/bufferinfo/legacy/BufferInfoMaliHisi.cpp b/bufferinfo/legacy/BufferInfoMaliHisi.cpp
index 5fc413a..1c7f4d0 100644
--- a/bufferinfo/legacy/BufferInfoMaliHisi.cpp
+++ b/bufferinfo/legacy/BufferInfoMaliHisi.cpp
@@ -66,33 +66,33 @@
 }
 #endif
 
-int BufferInfoMaliHisi::ConvertBoInfo(buffer_handle_t handle,
-                                      hwc_drm_bo_t *bo) {
+auto BufferInfoMaliHisi::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   bool is_rgb = false;
 
   const auto *hnd = (private_handle_t const *)handle;
   if (!hnd)
-    return -EINVAL;
+    return {};
 
   if (!(hnd->usage & GRALLOC_USAGE_HW_FB))
-    return -EINVAL;
+    return {};
 
   uint32_t fmt = ConvertHalFormatToDrm(hnd->req_format);
   if (fmt == DRM_FORMAT_INVALID)
-    return -EINVAL;
+    return {};
+
+  BufferInfo bi{};
 
   is_rgb = IsDrmFormatRgb(fmt);
-  bo->modifiers[0] = ConvertGrallocFormatToDrmModifiers(hnd->internal_format,
-                                                        is_rgb);
+  bi.modifiers[0] = ConvertGrallocFormatToDrmModifiers(hnd->internal_format,
+                                                       is_rgb);
 
-  bo->width = hnd->width;
-  bo->height = hnd->height;
-  bo->hal_format = hnd->req_format;
-  bo->format = fmt;
-  bo->usage = hnd->usage;
-  bo->pitches[0] = hnd->byte_stride;
-  bo->prime_fds[0] = hnd->share_fd;
-  bo->offsets[0] = 0;
+  bi.width = hnd->width;
+  bi.height = hnd->height;
+  bi.format = fmt;
+  bi.pitches[0] = hnd->byte_stride;
+  bi.prime_fds[0] = hnd->share_fd;
+  bi.offsets[0] = 0;
 
   switch (fmt) {
     case DRM_FORMAT_YVU420: {
@@ -106,20 +106,20 @@
       int v_size = vu_stride * (adjusted_height / 2);
 
       /* V plane*/
-      bo->prime_fds[1] = hnd->share_fd;
-      bo->pitches[1] = vu_stride;
-      bo->offsets[1] = y_size;
+      bi.prime_fds[1] = hnd->share_fd;
+      bi.pitches[1] = vu_stride;
+      bi.offsets[1] = y_size;
       /* U plane */
-      bo->prime_fds[2] = hnd->share_fd;
-      bo->pitches[2] = vu_stride;
-      bo->offsets[2] = y_size + v_size;
+      bi.prime_fds[2] = hnd->share_fd;
+      bi.pitches[2] = vu_stride;
+      bi.offsets[2] = y_size + v_size;
       break;
     }
     default:
       break;
   }
 
-  return 0;
+  return bi;
 }
 
 }  // namespace android
diff --git a/bufferinfo/legacy/BufferInfoMaliHisi.h b/bufferinfo/legacy/BufferInfoMaliHisi.h
index 698a0d3..cc37491 100644
--- a/bufferinfo/legacy/BufferInfoMaliHisi.h
+++ b/bufferinfo/legacy/BufferInfoMaliHisi.h
@@ -27,7 +27,7 @@
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
 
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
 
  private:
   uint64_t ConvertGrallocFormatToDrmModifiers(uint64_t flags, bool is_rgb);
diff --git a/bufferinfo/legacy/BufferInfoMaliMediatek.cpp b/bufferinfo/legacy/BufferInfoMaliMediatek.cpp
index 7e6f3a8..2e10460 100644
--- a/bufferinfo/legacy/BufferInfoMaliMediatek.cpp
+++ b/bufferinfo/legacy/BufferInfoMaliMediatek.cpp
@@ -32,26 +32,26 @@
 
 LEGACY_BUFFER_INFO_GETTER(BufferInfoMaliMediatek);
 
-int BufferInfoMaliMediatek::ConvertBoInfo(buffer_handle_t handle,
-                                          hwc_drm_bo_t *bo) {
+auto BufferInfoMaliMediatek::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   const auto *hnd = (private_handle_t const *)handle;
   if (!hnd)
-    return -EINVAL;
+    return {};
 
   uint32_t fmt = ConvertHalFormatToDrm(hnd->req_format);
   if (fmt == DRM_FORMAT_INVALID)
-    return -EINVAL;
+    return {};
 
-  bo->width = hnd->width;
-  bo->height = hnd->height;
-  bo->hal_format = hnd->req_format;
-  bo->format = fmt;
-  bo->usage = hnd->consumer_usage | hnd->producer_usage;
-  bo->prime_fds[0] = hnd->share_fd;
-  bo->pitches[0] = hnd->byte_stride;
-  bo->offsets[0] = 0;
+  BufferInfo bi{};
 
-  return 0;
+  bi.width = hnd->width;
+  bi.height = hnd->height;
+  bi.format = fmt;
+  bi.prime_fds[0] = hnd->share_fd;
+  bi.pitches[0] = hnd->byte_stride;
+  bi.offsets[0] = 0;
+
+  return bi;
 }
 
 }  // namespace android
diff --git a/bufferinfo/legacy/BufferInfoMaliMediatek.h b/bufferinfo/legacy/BufferInfoMaliMediatek.h
index 1204818..43d987a 100644
--- a/bufferinfo/legacy/BufferInfoMaliMediatek.h
+++ b/bufferinfo/legacy/BufferInfoMaliMediatek.h
@@ -27,7 +27,7 @@
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
 
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
 };
 }  // namespace android
 
diff --git a/bufferinfo/legacy/BufferInfoMaliMeson.cpp b/bufferinfo/legacy/BufferInfoMaliMeson.cpp
index 9daf542..8160296 100644
--- a/bufferinfo/legacy/BufferInfoMaliMeson.cpp
+++ b/bufferinfo/legacy/BufferInfoMaliMeson.cpp
@@ -61,32 +61,32 @@
 }
 #endif
 
-int BufferInfoMaliMeson::ConvertBoInfo(buffer_handle_t handle,
-                                       hwc_drm_bo_t *bo) {
+auto BufferInfoMaliMeson::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   const auto *hnd = (private_handle_t const *)handle;
   if (!hnd)
-    return -EINVAL;
+    return {};
 
   if (!(hnd->usage & GRALLOC_USAGE_HW_FB))
-    return -EINVAL;
+    return {};
 
   uint32_t fmt = ConvertHalFormatToDrm(hnd->req_format);
   if (fmt == DRM_FORMAT_INVALID)
-    return -EINVAL;
+    return {};
 
-  bo->modifiers[0] = BufferInfoMaliMeson::ConvertGrallocFormatToDrmModifiers(
+  BufferInfo bi{};
+
+  bi.modifiers[0] = BufferInfoMaliMeson::ConvertGrallocFormatToDrmModifiers(
       hnd->internal_format);
 
-  bo->width = hnd->width;
-  bo->height = hnd->height;
-  bo->hal_format = hnd->req_format;
-  bo->format = fmt;
-  bo->usage = hnd->usage;
-  bo->prime_fds[0] = hnd->share_fd;
-  bo->pitches[0] = hnd->byte_stride;
-  bo->offsets[0] = 0;
+  bi.width = hnd->width;
+  bi.height = hnd->height;
+  bi.format = fmt;
+  bi.prime_fds[0] = hnd->share_fd;
+  bi.pitches[0] = hnd->byte_stride;
+  bi.offsets[0] = 0;
 
-  return 0;
+  return bi;
 }
 
 }  // namespace android
diff --git a/bufferinfo/legacy/BufferInfoMaliMeson.h b/bufferinfo/legacy/BufferInfoMaliMeson.h
index ce5d3f9..3b6fab0 100644
--- a/bufferinfo/legacy/BufferInfoMaliMeson.h
+++ b/bufferinfo/legacy/BufferInfoMaliMeson.h
@@ -26,7 +26,7 @@
 class BufferInfoMaliMeson : public LegacyBufferInfoGetter {
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
 
  private:
   uint64_t ConvertGrallocFormatToDrmModifiers(uint64_t flags);
diff --git a/bufferinfo/legacy/BufferInfoMinigbm.cpp b/bufferinfo/legacy/BufferInfoMinigbm.cpp
index 777c2b7..c5a9e98 100644
--- a/bufferinfo/legacy/BufferInfoMinigbm.cpp
+++ b/bufferinfo/legacy/BufferInfoMinigbm.cpp
@@ -43,11 +43,14 @@
   int stride[4];
 };
 
-int BufferInfoMinigbm::ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) {
+auto BufferInfoMinigbm::GetBoInfo(buffer_handle_t handle)
+    -> std::optional<BufferInfo> {
   if (handle == nullptr) {
-    return -EINVAL;
+    return {};
   }
 
+  BufferInfo bi{};
+
   uint32_t width{};
   uint32_t height{};
   if (gralloc_->perform(gralloc_, CROS_GRALLOC_DRM_GET_DIMENSIONS, handle,
@@ -55,7 +58,7 @@
     ALOGE(
         "CROS_GRALLOC_DRM_GET_DIMENSIONS operation has failed. "
         "Please ensure you are using the latest minigbm.");
-    return -EINVAL;
+    return {};
   }
 
   int32_t droid_format{};
@@ -64,7 +67,7 @@
     ALOGE(
         "CROS_GRALLOC_DRM_GET_FORMAT operation has failed. "
         "Please ensure you are using the latest minigbm.");
-    return -EINVAL;
+    return {};
   }
 
   uint32_t usage{};
@@ -73,7 +76,7 @@
     ALOGE(
         "CROS_GRALLOC_DRM_GET_USAGE operation has failed. "
         "Please ensure you are using the latest minigbm.");
-    return -EINVAL;
+    return {};
   }
 
   struct cros_gralloc0_buffer_info info {};
@@ -82,25 +85,22 @@
     ALOGE(
         "CROS_GRALLOC_DRM_GET_BUFFER_INFO operation has failed. "
         "Please ensure you are using the latest minigbm.");
-    return -EINVAL;
+    return {};
   }
 
-  bo->width = width;
-  bo->height = height;
+  bi.width = width;
+  bi.height = height;
 
-  bo->hal_format = droid_format;
-
-  bo->format = info.drm_fourcc;
-  bo->usage = usage;
+  bi.format = info.drm_fourcc;
 
   for (int i = 0; i < info.num_fds; i++) {
-    bo->modifiers[i] = info.modifier;
-    bo->prime_fds[i] = info.fds[i];
-    bo->pitches[i] = info.stride[i];
-    bo->offsets[i] = info.offset[i];
+    bi.modifiers[i] = info.modifier;
+    bi.prime_fds[i] = info.fds[i];
+    bi.pitches[i] = info.stride[i];
+    bi.offsets[i] = info.offset[i];
   }
 
-  return 0;
+  return bi;
 }
 
 constexpr char cros_gralloc_module_name[] = "CrOS Gralloc";
diff --git a/bufferinfo/legacy/BufferInfoMinigbm.h b/bufferinfo/legacy/BufferInfoMinigbm.h
index 04cc2ae..40d9926 100644
--- a/bufferinfo/legacy/BufferInfoMinigbm.h
+++ b/bufferinfo/legacy/BufferInfoMinigbm.h
@@ -26,7 +26,7 @@
 class BufferInfoMinigbm : public LegacyBufferInfoGetter {
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
-  int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  auto GetBoInfo(buffer_handle_t handle) -> std::optional<BufferInfo> override;
   int ValidateGralloc() override;
 };
 
diff --git a/compositor/DrmKmsPlan.cpp b/compositor/DrmKmsPlan.cpp
index 966bd4e..6289b84 100644
--- a/compositor/DrmKmsPlan.cpp
+++ b/compositor/DrmKmsPlan.cpp
@@ -24,7 +24,7 @@
 
 namespace android {
 auto DrmKmsPlan::CreateDrmKmsPlan(DrmDisplayPipeline &pipe,
-                                  std::vector<DrmHwcLayer> composition)
+                                  std::vector<LayerData> composition)
     -> std::unique_ptr<DrmKmsPlan> {
   auto plan = std::make_unique<DrmKmsPlan>();
 
diff --git a/compositor/DrmKmsPlan.h b/compositor/DrmKmsPlan.h
index 35e66e9..91f636e 100644
--- a/compositor/DrmKmsPlan.h
+++ b/compositor/DrmKmsPlan.h
@@ -20,7 +20,7 @@
 #include <memory>
 #include <vector>
 
-#include "drmhwcomposer.h"
+#include "LayerData.h"
 
 namespace android {
 
@@ -28,7 +28,7 @@
 
 struct DrmKmsPlan {
   struct LayerToPlaneJoining {
-    DrmHwcLayer layer;
+    LayerData layer;
     std::shared_ptr<BindingOwner<DrmPlane>> plane;
     int z_pos;
   };
@@ -36,7 +36,7 @@
   std::vector<LayerToPlaneJoining> plan;
 
   static auto CreateDrmKmsPlan(DrmDisplayPipeline &pipe,
-                               std::vector<DrmHwcLayer> composition)
+                               std::vector<LayerData> composition)
       -> std::unique_ptr<DrmKmsPlan>;
 };
 
diff --git a/compositor/LayerData.h b/compositor/LayerData.h
new file mode 100644
index 0000000..d04514d
--- /dev/null
+++ b/compositor/LayerData.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 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 <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+#include <cmath>
+#include <cstdbool>
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+#include "bufferinfo/BufferInfo.h"
+#include "drm/DrmFbImporter.h"
+#include "utils/UniqueFd.h"
+
+namespace android {
+
+class DrmFbIdHandle;
+
+enum LayerTransform : uint32_t {
+  kIdentity = 0,
+  kFlipH = 1 << 0,
+  kFlipV = 1 << 1,
+  kRotate90 = 1 << 2,
+  kRotate180 = 1 << 3,
+  kRotate270 = 1 << 4,
+};
+
+struct PresentInfo {
+  LayerTransform transform{};
+  uint16_t alpha = UINT16_MAX;
+  hwc_frect_t source_crop{};
+  hwc_rect_t display_frame{};
+
+  bool RequireScalingOrPhasing() const {
+    float src_width = source_crop.right - source_crop.left;
+    float src_height = source_crop.bottom - source_crop.top;
+
+    auto dest_width = float(display_frame.right - display_frame.left);
+    auto dest_height = float(display_frame.bottom - display_frame.top);
+
+    bool scaling = src_width != dest_width || src_height != dest_height;
+    bool phasing = (source_crop.left - std::floor(source_crop.left) != 0) ||
+                   (source_crop.top - std::floor(source_crop.top) != 0);
+    return scaling || phasing;
+  }
+};
+
+struct LayerData {
+  auto Clone() {
+    LayerData clonned;
+    clonned.bi = bi;
+    clonned.fb = fb;
+    clonned.pi = pi;
+    clonned.acquire_fence = std::move(acquire_fence);
+    return clonned;
+  }
+
+  std::optional<BufferInfo> bi;
+  std::shared_ptr<DrmFbIdHandle> fb;
+  PresentInfo pi;
+  UniqueFd acquire_fence;
+};
+
+}  // namespace android
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 65fb19e..5d2eebd 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#undef NDEBUG /* Required for assert to work */
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #define LOG_TAG "hwc-drm-atomic-state-manager"
 
@@ -26,6 +28,7 @@
 #include <utils/Trace.h>
 
 #include <array>
+#include <cassert>
 #include <cstdlib>
 #include <ctime>
 #include <sstream>
@@ -70,13 +73,15 @@
     return -ENOMEM;
   }
 
-  int64_t out_fence = -1;
-  if (crtc->GetOutFencePtrProperty() &&
-      !crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) {
+  int out_fence = -1;
+  if (!crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) {
     return -EINVAL;
   }
 
+  bool nonblock = true;
+
   if (args.active) {
+    nonblock = false;
     new_frame_state.crtc_active_state = *args.active;
     if (!crtc->GetActiveProperty().AtomicSet(*pset, *args.active ? 1 : 0) ||
         !connector->GetCrtcIdProperty().AtomicSet(*pset, crtc->GetId())) {
@@ -100,14 +105,13 @@
   auto unused_planes = new_frame_state.used_planes;
 
   if (args.composition) {
-    new_frame_state.used_framebuffers.clear();
     new_frame_state.used_planes.clear();
 
     for (auto &joining : args.composition->plan) {
       DrmPlane *plane = joining.plane->Get();
-      DrmHwcLayer &layer = joining.layer;
+      LayerData &layer = joining.layer;
 
-      new_frame_state.used_framebuffers.emplace_back(layer.fb_id_handle);
+      new_frame_state.used_framebuffers.emplace_back(layer.fb);
       new_frame_state.used_planes.emplace_back(joining.plane);
 
       /* Remove from 'unused' list, since plane is re-used */
@@ -130,33 +134,128 @@
   }
 
   uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
-  if (args.test_only)
-    flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+
+  if (args.test_only) {
+    return drmModeAtomicCommit(drm->GetFd(), pset.get(),
+                               flags | DRM_MODE_ATOMIC_TEST_ONLY, drm);
+  }
+
+  if (last_present_fence_) {
+    ATRACE_NAME("WaitPriorFramePresented");
+
+    constexpr int kTimeoutMs = 500;
+    int err = sync_wait(last_present_fence_.Get(), kTimeoutMs);
+    if (err != 0) {
+      ALOGE("sync_wait(fd=%i) returned: %i (errno: %i)",
+            last_present_fence_.Get(), err, errno);
+    }
+
+    CleanupPriorFrameResources();
+  }
+
+  if (nonblock) {
+    flags |= DRM_MODE_ATOMIC_NONBLOCK;
+  }
 
   int err = drmModeAtomicCommit(drm->GetFd(), pset.get(), flags, drm);
+
   if (err != 0) {
-    if (!args.test_only)
-      ALOGE("Failed to commit pset ret=%d\n", err);
+    ALOGE("Failed to commit pset ret=%d\n", err);
     return err;
   }
 
-  if (!args.test_only) {
-    if (args.display_mode) {
-      /* TODO(nobody): we still need this for synthetic vsync, remove after
-       * vsync reworked */
-      connector->SetActiveMode(*args.display_mode);
-    }
-
+  if (nonblock) {
+    last_present_fence_ = UniqueFd::Dup(out_fence);
+    staged_frame_state_ = std::move(new_frame_state);
+    frames_staged_++;
+    ptt_->Notify();
+  } else {
     active_frame_state_ = std::move(new_frame_state);
-
-    if (crtc->GetOutFencePtrProperty()) {
-      args.out_fence = UniqueFd((int)out_fence);
-    }
   }
 
+  if (args.display_mode) {
+    /* TODO(nobody): we still need this for synthetic vsync, remove after
+     * vsync reworked */
+    connector->SetActiveMode(*args.display_mode);
+  }
+
+  args.out_fence = UniqueFd(out_fence);
+
   return 0;
 }
 
+PresentTrackerThread::PresentTrackerThread(DrmAtomicStateManager *st_man)
+    : st_man_(st_man),
+      mutex_(&st_man_->pipe_->device->GetResMan().GetMainLock()) {
+  pt_ = std::thread(&PresentTrackerThread::PresentTrackerThreadFn, this);
+}
+
+PresentTrackerThread::~PresentTrackerThread() {
+  ALOGI("PresentTrackerThread successfully destroyed");
+}
+
+void PresentTrackerThread::PresentTrackerThreadFn() {
+  /* object should be destroyed on thread exit */
+  auto self = std::unique_ptr<PresentTrackerThread>(this);
+
+  int tracking_at_the_moment = -1;
+
+  for (;;) {
+    UniqueFd present_fence;
+
+    {
+      std::unique_lock lk(*mutex_);
+      cv_.wait(lk, [&] {
+        return st_man_ == nullptr ||
+               st_man_->frames_staged_ > tracking_at_the_moment;
+      });
+
+      if (st_man_ == nullptr) {
+        break;
+      }
+
+      tracking_at_the_moment = st_man_->frames_staged_;
+
+      present_fence = UniqueFd::Dup(st_man_->last_present_fence_.Get());
+      if (!present_fence) {
+        continue;
+      }
+    }
+
+    {
+      ATRACE_NAME("AsyncWaitForBuffersSwap");
+      constexpr int kTimeoutMs = 500;
+      int err = sync_wait(present_fence.Get(), kTimeoutMs);
+      if (err != 0) {
+        ALOGE("sync_wait(fd=%i) returned: %i (errno: %i)", present_fence.Get(),
+              err, errno);
+      }
+    }
+
+    {
+      std::unique_lock lk(*mutex_);
+      if (st_man_ == nullptr) {
+        break;
+      }
+
+      /* If resources is already cleaned-up by main thread, skip */
+      if (tracking_at_the_moment > st_man_->frames_tracked_) {
+        st_man_->CleanupPriorFrameResources();
+      }
+    }
+  }
+}
+
+void DrmAtomicStateManager::CleanupPriorFrameResources() {
+  assert(frames_staged_ - frames_tracked_ == 1);
+  assert(last_present_fence_);
+
+  ATRACE_NAME("CleanupPriorFrameResources");
+  frames_tracked_++;
+  active_frame_state_ = std::move(staged_frame_state_);
+  last_present_fence_ = {};
+}
+
 auto DrmAtomicStateManager::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int {
   int err = CommitFrame(args);
 
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 08a1c13..b0b85ac 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -26,10 +26,10 @@
 #include <tuple>
 
 #include "compositor/DrmKmsPlan.h"
+#include "compositor/LayerData.h"
 #include "drm/DrmPlane.h"
 #include "drm/ResourceManager.h"
 #include "drm/VSyncWorker.h"
-#include "drmhwcomposer.h"
 
 namespace android {
 
@@ -49,11 +49,45 @@
   }
 };
 
-class DrmAtomicStateManager {
+class PresentTrackerThread {
  public:
-  explicit DrmAtomicStateManager(DrmDisplayPipeline *pipe) : pipe_(pipe){};
+  explicit PresentTrackerThread(DrmAtomicStateManager *st_man);
+
+  ~PresentTrackerThread();
+
+  void Stop() {
+    /* Exit thread by signalling that object is no longer valid */
+    st_man_ = nullptr;
+    Notify();
+    pt_.detach();
+  }
+
+  void Notify() {
+    cv_.notify_all();
+  }
+
+ private:
+  DrmAtomicStateManager *st_man_{};
+
+  void PresentTrackerThreadFn();
+
+  std::condition_variable cv_;
+  std::thread pt_;
+  std::mutex *mutex_;
+};
+
+class DrmAtomicStateManager {
+  friend class PresentTrackerThread;
+
+ public:
+  explicit DrmAtomicStateManager(DrmDisplayPipeline *pipe)
+      : pipe_(pipe),
+        ptt_(std::make_unique<PresentTrackerThread>(this).release()){};
+
   DrmAtomicStateManager(const DrmAtomicStateManager &) = delete;
-  ~DrmAtomicStateManager() = default;
+  ~DrmAtomicStateManager() {
+    ptt_->Stop();
+  }
 
   auto ExecuteAtomicCommit(AtomicCommitArgs &args) -> int;
   auto ActivateDisplayUsingDPMS() -> int;
@@ -70,20 +104,32 @@
 
     DrmModeUserPropertyBlobUnique mode_blob;
 
+    int release_fence_pt_index{};
+
     /* To avoid setting the inactive state twice, which will fail the commit */
     bool crtc_active_state{};
   } active_frame_state_;
 
   auto NewFrameState() -> KmsState {
+    auto *prev_frame_state = &active_frame_state_;
     return (KmsState){
-        .used_planes = active_frame_state_.used_planes,
-        .used_framebuffers = active_frame_state_.used_framebuffers,
-        .crtc_active_state = active_frame_state_.crtc_active_state,
+        .used_planes = prev_frame_state->used_planes,
+        .crtc_active_state = prev_frame_state->crtc_active_state,
     };
   }
 
   DrmDisplayPipeline *const pipe_;
+
+  void CleanupPriorFrameResources();
+
+  /* Present (swap) tracking */
+  PresentTrackerThread *ptt_;
+  KmsState staged_frame_state_;
+  UniqueFd last_present_fence_;
+  int frames_staged_{};
+  int frames_tracked_{};
 };
+
 }  // namespace android
 
 #endif  // ANDROID_DRM_DISPLAY_COMPOSITOR_H_
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index fd4589e..0f73f1f 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -18,7 +18,6 @@
 
 #include "DrmDevice.h"
 
-#include <fcntl.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -28,12 +27,29 @@
 
 #include "drm/DrmAtomicStateManager.h"
 #include "drm/DrmPlane.h"
+#include "drm/ResourceManager.h"
 #include "utils/log.h"
 #include "utils/properties.h"
 
 namespace android {
 
-DrmDevice::DrmDevice() {
+auto DrmDevice::CreateInstance(std::string const &path,
+                               ResourceManager *res_man)
+    -> std::unique_ptr<DrmDevice> {
+  if (!IsKMSDev(path.c_str())) {
+    return {};
+  }
+
+  auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man));
+
+  if (device->Init(path.c_str()) != 0) {
+    return {};
+  }
+
+  return device;
+}
+
+DrmDevice::DrmDevice(ResourceManager *res_man) : res_man_(res_man) {
   drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
 }
 
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index f2530ee..4cf0132 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -31,18 +31,23 @@
 
 class DrmFbImporter;
 class DrmPlane;
+class ResourceManager;
 
 class DrmDevice {
  public:
-  DrmDevice();
   ~DrmDevice() = default;
 
-  auto Init(const char *path) -> int;
+  static auto CreateInstance(std::string const &path, ResourceManager *res_man)
+      -> std::unique_ptr<DrmDevice>;
 
   auto GetFd() const {
     return fd_.Get();
   }
 
+  auto &GetResMan() {
+    return *res_man_;
+  }
+
   auto GetConnectors() -> const std::vector<std::unique_ptr<DrmConnector>> &;
   auto GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> &;
   auto GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> &;
@@ -69,8 +74,6 @@
     return *drm_fb_importer_;
   }
 
-  static auto IsKMSDev(const char *path) -> bool;
-
   auto FindCrtcById(uint32_t id) const -> DrmCrtc * {
     for (const auto &crtc : crtcs_) {
       if (crtc->GetId() == id) {
@@ -95,6 +98,11 @@
                   DrmProperty *property) const;
 
  private:
+  explicit DrmDevice(ResourceManager *res_man);
+  auto Init(const char *path) -> int;
+
+  static auto IsKMSDev(const char *path) -> bool;
+
   UniqueFd fd_;
 
   std::vector<std::unique_ptr<DrmConnector>> connectors_;
@@ -109,6 +117,8 @@
   bool HasAddFb2ModifiersSupport_{};
 
   std::unique_ptr<DrmFbImporter> drm_fb_importer_;
+
+  ResourceManager *const res_man_;
 };
 }  // namespace android
 
diff --git a/drm/DrmFbImporter.cpp b/drm/DrmFbImporter.cpp
index eeab076..585b789 100644
--- a/drm/DrmFbImporter.cpp
+++ b/drm/DrmFbImporter.cpp
@@ -15,11 +15,14 @@
  */
 
 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
 #define LOG_TAG "hwc-platform-drm-generic"
 
 #include "DrmFbImporter.h"
 
 #include <hardware/gralloc.h>
+#include <utils/Trace.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -31,9 +34,11 @@
 
 namespace android {
 
-auto DrmFbIdHandle::CreateInstance(hwc_drm_bo_t *bo, GemHandle first_gem_handle,
+auto DrmFbIdHandle::CreateInstance(BufferInfo *bo, GemHandle first_gem_handle,
                                    DrmDevice &drm)
     -> std::shared_ptr<DrmFbIdHandle> {
+  ATRACE_NAME("Import dmabufs and register FB");
+
   // NOLINTNEXTLINE(cppcoreguidelines-owning-memory): priv. constructor usage
   std::shared_ptr<DrmFbIdHandle> local(new DrmFbIdHandle(drm));
 
@@ -69,11 +74,11 @@
   /* Create framebuffer object */
   if (!has_modifiers) {
     err = drmModeAddFB2(drm.GetFd(), bo->width, bo->height, bo->format,
-                        &local->gem_handles_[0], &bo->pitches[0],
+                        local->gem_handles_.data(), &bo->pitches[0],
                         &bo->offsets[0], &local->fb_id_, 0);
   } else {
     err = drmModeAddFB2WithModifiers(drm.GetFd(), bo->width, bo->height,
-                                     bo->format, &local->gem_handles_[0],
+                                     bo->format, local->gem_handles_.data(),
                                      &bo->pitches[0], &bo->offsets[0],
                                      &bo->modifiers[0], &local->fb_id_,
                                      DRM_MODE_FB_MODIFIERS);
@@ -87,6 +92,8 @@
 }
 
 DrmFbIdHandle::~DrmFbIdHandle() {
+  ATRACE_NAME("Close FB and dmabufs");
+
   /* Destroy framebuffer object */
   if (drmModeRmFB(drm_->GetFd(), fb_id_) != 0) {
     ALOGE("Failed to rm fb");
@@ -116,7 +123,7 @@
   }
 }
 
-auto DrmFbImporter::GetOrCreateFbId(hwc_drm_bo_t *bo)
+auto DrmFbImporter::GetOrCreateFbId(BufferInfo *bo)
     -> std::shared_ptr<DrmFbIdHandle> {
   /* Lookup DrmFbIdHandle in cache first. First handle serves as a cache key. */
   GemHandle first_handle = 0;
diff --git a/drm/DrmFbImporter.h b/drm/DrmFbImporter.h
index 7f17bbe..9e94238 100644
--- a/drm/DrmFbImporter.h
+++ b/drm/DrmFbImporter.h
@@ -23,8 +23,8 @@
 #include <array>
 #include <map>
 
+#include "bufferinfo/BufferInfo.h"
 #include "drm/DrmDevice.h"
-#include "drmhwcgralloc.h"
 
 #ifndef DRM_FORMAT_INVALID
 #define DRM_FORMAT_INVALID 0
@@ -36,7 +36,7 @@
 
 class DrmFbIdHandle {
  public:
-  static auto CreateInstance(hwc_drm_bo_t *bo, GemHandle first_gem_handle,
+  static auto CreateInstance(BufferInfo *bo, GemHandle first_gem_handle,
                              DrmDevice &drm) -> std::shared_ptr<DrmFbIdHandle>;
 
   ~DrmFbIdHandle();
@@ -55,7 +55,7 @@
   DrmDevice *const drm_;
 
   uint32_t fb_id_{};
-  std::array<GemHandle, kHwcDrmBoMaxPlanes> gem_handles_{};
+  std::array<GemHandle, kBufferMaxPlanes> gem_handles_{};
 };
 
 class DrmFbImporter {
@@ -67,7 +67,7 @@
   auto operator=(const DrmFbImporter &) = delete;
   auto operator=(DrmFbImporter &&) = delete;
 
-  auto GetOrCreateFbId(hwc_drm_bo_t *bo) -> std::shared_ptr<DrmFbIdHandle>;
+  auto GetOrCreateFbId(BufferInfo *bo) -> std::shared_ptr<DrmFbIdHandle>;
 
  private:
   void CleanupEmptyCacheElements() {
diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp
index 28f48f3..329ef1c 100644
--- a/drm/DrmPlane.cpp
+++ b/drm/DrmPlane.cpp
@@ -91,17 +91,17 @@
   GetPlaneProperty("zpos", zpos_property_, Presence::kOptional);
 
   if (GetPlaneProperty("rotation", rotation_property_, Presence::kOptional)) {
-    rotation_property_.AddEnumToMap("rotate-0", DrmHwcTransform::kIdentity,
+    rotation_property_.AddEnumToMap("rotate-0", LayerTransform::kIdentity,
                                     transform_enum_map_);
-    rotation_property_.AddEnumToMap("rotate-90", DrmHwcTransform::kRotate90,
+    rotation_property_.AddEnumToMap("rotate-90", LayerTransform::kRotate90,
                                     transform_enum_map_);
-    rotation_property_.AddEnumToMap("rotate-180", DrmHwcTransform::kRotate180,
+    rotation_property_.AddEnumToMap("rotate-180", LayerTransform::kRotate180,
                                     transform_enum_map_);
-    rotation_property_.AddEnumToMap("rotate-270", DrmHwcTransform::kRotate270,
+    rotation_property_.AddEnumToMap("rotate-270", LayerTransform::kRotate270,
                                     transform_enum_map_);
-    rotation_property_.AddEnumToMap("reflect-x", DrmHwcTransform::kFlipH,
+    rotation_property_.AddEnumToMap("reflect-x", LayerTransform::kFlipH,
                                     transform_enum_map_);
-    rotation_property_.AddEnumToMap("reflect-y", DrmHwcTransform::kFlipV,
+    rotation_property_.AddEnumToMap("reflect-y", LayerTransform::kFlipV,
                                     transform_enum_map_);
   }
 
@@ -109,11 +109,11 @@
 
   if (GetPlaneProperty("pixel blend mode", blend_property_,
                        Presence::kOptional)) {
-    blend_property_.AddEnumToMap("Pre-multiplied", DrmHwcBlending::kPreMult,
+    blend_property_.AddEnumToMap("Pre-multiplied", BufferBlendMode::kPreMult,
                                  blending_enum_map_);
-    blend_property_.AddEnumToMap("Coverage", DrmHwcBlending::kCoverage,
+    blend_property_.AddEnumToMap("Coverage", BufferBlendMode::kCoverage,
                                  blending_enum_map_);
-    blend_property_.AddEnumToMap("None", DrmHwcBlending::kNone,
+    blend_property_.AddEnumToMap("None", BufferBlendMode::kNone,
                                  blending_enum_map_);
   }
 
@@ -123,23 +123,23 @@
     if (GetPlaneProperty("COLOR_ENCODING", color_encoding_propery_,
                          Presence::kOptional)) {
       color_encoding_propery_.AddEnumToMap("ITU-R BT.709 YCbCr",
-                                           DrmHwcColorSpace::kItuRec709,
+                                           BufferColorSpace::kItuRec709,
                                            color_encoding_enum_map_);
       color_encoding_propery_.AddEnumToMap("ITU-R BT.601 YCbCr",
-                                           DrmHwcColorSpace::kItuRec601,
+                                           BufferColorSpace::kItuRec601,
                                            color_encoding_enum_map_);
       color_encoding_propery_.AddEnumToMap("ITU-R BT.2020 YCbCr",
-                                           DrmHwcColorSpace::kItuRec2020,
+                                           BufferColorSpace::kItuRec2020,
                                            color_encoding_enum_map_);
     }
 
     if (GetPlaneProperty("COLOR_RANGE", color_range_property_,
                          Presence::kOptional)) {
       color_range_property_.AddEnumToMap("YCbCr full range",
-                                         DrmHwcSampleRange::kFullRange,
+                                         BufferSampleRange::kFullRange,
                                          color_range_enum_map_);
       color_range_property_.AddEnumToMap("YCbCr limited range",
-                                         DrmHwcSampleRange::kLimitedRange,
+                                         BufferSampleRange::kLimitedRange,
                                          color_range_enum_map_);
     }
   }
@@ -151,32 +151,32 @@
   return ((1 << crtc.GetIndexInResArray()) & plane_->possible_crtcs) != 0;
 }
 
-bool DrmPlane::IsValidForLayer(DrmHwcLayer *layer) {
+bool DrmPlane::IsValidForLayer(LayerData *layer) {
   if (!rotation_property_) {
-    if (layer->transform != DrmHwcTransform::kIdentity) {
+    if (layer->pi.transform != LayerTransform::kIdentity) {
       ALOGV("No rotation property on plane %d", GetId());
       return false;
     }
   } else {
-    if (transform_enum_map_.count(layer->transform) == 0) {
+    if (transform_enum_map_.count(layer->pi.transform) == 0) {
       ALOGV("Transform is not supported on plane %d", GetId());
       return false;
     }
   }
 
-  if (alpha_property_.id() == 0 && layer->alpha != UINT16_MAX) {
+  if (alpha_property_.id() == 0 && layer->pi.alpha != UINT16_MAX) {
     ALOGV("Alpha is not supported on plane %d", GetId());
     return false;
   }
 
-  if (blending_enum_map_.count(layer->blending) == 0 &&
-      layer->blending != DrmHwcBlending::kNone &&
-      layer->blending != DrmHwcBlending::kPreMult) {
+  if (blending_enum_map_.count(layer->bi->blend_mode) == 0 &&
+      layer->bi->blend_mode != BufferBlendMode::kNone &&
+      layer->bi->blend_mode != BufferBlendMode::kPreMult) {
     ALOGV("Blending is not supported on plane %d", GetId());
     return false;
   }
 
-  uint32_t format = layer->buffer_info.format;
+  uint32_t format = layer->bi->format;
   if (!IsFormatSupported(format)) {
     ALOGV("Plane %d does not supports %c%c%c%c format", GetId(), format,
           format >> 8, format >> 16, format >> 24);
@@ -198,17 +198,17 @@
                           }) != std::end(formats_);
 }
 
-static uint64_t ToDrmRotation(DrmHwcTransform transform) {
+static uint64_t ToDrmRotation(LayerTransform transform) {
   uint64_t rotation = 0;
-  if ((transform & DrmHwcTransform::kFlipH) != 0)
+  if ((transform & LayerTransform::kFlipH) != 0)
     rotation |= DRM_MODE_REFLECT_X;
-  if ((transform & DrmHwcTransform::kFlipV) != 0)
+  if ((transform & LayerTransform::kFlipV) != 0)
     rotation |= DRM_MODE_REFLECT_Y;
-  if ((transform & DrmHwcTransform::kRotate90) != 0)
+  if ((transform & LayerTransform::kRotate90) != 0)
     rotation |= DRM_MODE_ROTATE_90;
-  else if ((transform & DrmHwcTransform::kRotate180) != 0)
+  else if ((transform & LayerTransform::kRotate180) != 0)
     rotation |= DRM_MODE_ROTATE_180;
-  else if ((transform & DrmHwcTransform::kRotate270) != 0)
+  else if ((transform & LayerTransform::kRotate270) != 0)
     rotation |= DRM_MODE_ROTATE_270;
   else
     rotation |= DRM_MODE_ROTATE_0;
@@ -222,9 +222,9 @@
   return int(in * (1 << kBitShift));
 }
 
-auto DrmPlane::AtomicSetState(drmModeAtomicReq &pset, DrmHwcLayer &layer,
+auto DrmPlane::AtomicSetState(drmModeAtomicReq &pset, LayerData &layer,
                               uint32_t zpos, uint32_t crtc_id) -> int {
-  if (!layer.fb_id_handle) {
+  if (!layer.fb) {
     ALOGE("Expected a valid framebuffer for pset");
     return -EINVAL;
   }
@@ -245,46 +245,45 @@
     return -EINVAL;
   }
 
+  auto &disp = layer.pi.display_frame;
+  auto &src = layer.pi.source_crop;
   if (!crtc_property_.AtomicSet(pset, crtc_id) ||
-      !fb_property_.AtomicSet(pset, layer.fb_id_handle->GetFbId()) ||
-      !crtc_x_property_.AtomicSet(pset, layer.display_frame.left) ||
-      !crtc_y_property_.AtomicSet(pset, layer.display_frame.top) ||
-      !crtc_w_property_.AtomicSet(pset, layer.display_frame.right -
-                                            layer.display_frame.left) ||
-      !crtc_h_property_.AtomicSet(pset, layer.display_frame.bottom -
-                                            layer.display_frame.top) ||
-      !src_x_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.left)) ||
-      !src_y_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.top)) ||
-      !src_w_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.right -
-                                                   layer.source_crop.left)) ||
-      !src_h_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.bottom -
-                                                   layer.source_crop.top))) {
+      !fb_property_.AtomicSet(pset, layer.fb->GetFbId()) ||
+      !crtc_x_property_.AtomicSet(pset, disp.left) ||
+      !crtc_y_property_.AtomicSet(pset, disp.top) ||
+      !crtc_w_property_.AtomicSet(pset, disp.right - disp.left) ||
+      !crtc_h_property_.AtomicSet(pset, disp.bottom - disp.top) ||
+      !src_x_property_.AtomicSet(pset, To1616FixPt(src.left)) ||
+      !src_y_property_.AtomicSet(pset, To1616FixPt(src.top)) ||
+      !src_w_property_.AtomicSet(pset, To1616FixPt(src.right - src.left)) ||
+      !src_h_property_.AtomicSet(pset, To1616FixPt(src.bottom - src.top))) {
     return -EINVAL;
   }
 
   if (rotation_property_ &&
-      !rotation_property_.AtomicSet(pset, ToDrmRotation(layer.transform))) {
+      !rotation_property_.AtomicSet(pset, ToDrmRotation(layer.pi.transform))) {
     return -EINVAL;
   }
 
-  if (alpha_property_ && !alpha_property_.AtomicSet(pset, layer.alpha)) {
+  if (alpha_property_ && !alpha_property_.AtomicSet(pset, layer.pi.alpha)) {
     return -EINVAL;
   }
 
-  if (blending_enum_map_.count(layer.blending) != 0 &&
-      !blend_property_.AtomicSet(pset, blending_enum_map_[layer.blending])) {
+  if (blending_enum_map_.count(layer.bi->blend_mode) != 0 &&
+      !blend_property_.AtomicSet(pset,
+                                 blending_enum_map_[layer.bi->blend_mode])) {
     return -EINVAL;
   }
 
-  if (color_encoding_enum_map_.count(layer.color_space) != 0 &&
+  if (color_encoding_enum_map_.count(layer.bi->color_space) != 0 &&
       !color_encoding_propery_
-           .AtomicSet(pset, color_encoding_enum_map_[layer.color_space])) {
+           .AtomicSet(pset, color_encoding_enum_map_[layer.bi->color_space])) {
     return -EINVAL;
   }
 
-  if (color_range_enum_map_.count(layer.sample_range) != 0 &&
+  if (color_range_enum_map_.count(layer.bi->sample_range) != 0 &&
       !color_range_property_
-           .AtomicSet(pset, color_range_enum_map_[layer.sample_range])) {
+           .AtomicSet(pset, color_range_enum_map_[layer.bi->sample_range])) {
     return -EINVAL;
   }
 
diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h
index 65f458f..31f0a33 100644
--- a/drm/DrmPlane.h
+++ b/drm/DrmPlane.h
@@ -24,12 +24,12 @@
 
 #include "DrmCrtc.h"
 #include "DrmProperty.h"
-#include "drmhwcomposer.h"
+#include "compositor/LayerData.h"
 
 namespace android {
 
 class DrmDevice;
-struct DrmHwcLayer;
+struct LayerData;
 
 class DrmPlane : public PipelineBindable<DrmPlane> {
  public:
@@ -40,7 +40,7 @@
       -> std::unique_ptr<DrmPlane>;
 
   bool IsCrtcSupported(const DrmCrtc &crtc) const;
-  bool IsValidForLayer(DrmHwcLayer *layer);
+  bool IsValidForLayer(LayerData *layer);
 
   auto GetType() const {
     return type_;
@@ -49,7 +49,7 @@
   bool IsFormatSupported(uint32_t format) const;
   bool HasNonRgbFormat() const;
 
-  auto AtomicSetState(drmModeAtomicReq &pset, DrmHwcLayer &layer, uint32_t zpos,
+  auto AtomicSetState(drmModeAtomicReq &pset, LayerData &layer, uint32_t zpos,
                       uint32_t crtc_id) -> int;
   auto AtomicDisablePlane(drmModeAtomicReq &pset) -> int;
   auto &GetZPosProperty() const {
@@ -94,10 +94,10 @@
   DrmProperty color_encoding_propery_;
   DrmProperty color_range_property_;
 
-  std::map<DrmHwcBlending, uint64_t> blending_enum_map_;
-  std::map<DrmHwcColorSpace, uint64_t> color_encoding_enum_map_;
-  std::map<DrmHwcSampleRange, uint64_t> color_range_enum_map_;
-  std::map<DrmHwcTransform, uint64_t> transform_enum_map_;
+  std::map<BufferBlendMode, uint64_t> blending_enum_map_;
+  std::map<BufferColorSpace, uint64_t> color_encoding_enum_map_;
+  std::map<BufferSampleRange, uint64_t> color_range_enum_map_;
+  std::map<LayerTransform, uint64_t> transform_enum_map_;
 };
 }  // namespace android
 
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index c8235e9..dbf0993 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -18,7 +18,6 @@
 
 #include "ResourceManager.h"
 
-#include <fcntl.h>
 #include <sys/stat.h>
 
 #include <ctime>
@@ -58,7 +57,10 @@
   int path_len = property_get("vendor.hwc.drm.device", path_pattern,
                               "/dev/dri/card%");
   if (path_pattern[path_len - 1] != '%') {
-    AddDrmDevice(std::string(path_pattern));
+    auto dev = DrmDevice::CreateInstance(path_pattern, this);
+    if (dev) {
+      drms_.emplace_back(std::move(dev));
+    }
   } else {
     path_pattern[path_len - 1] = '\0';
     for (int idx = 0;; ++idx) {
@@ -69,8 +71,9 @@
       if (stat(path.str().c_str(), &buf) != 0)
         break;
 
-      if (DrmDevice::IsKMSDev(path.str().c_str())) {
-        AddDrmDevice(path.str());
+      auto dev = DrmDevice::CreateInstance(path.str(), this);
+      if (dev) {
+        drms_.emplace_back(std::move(dev));
       }
     }
   }
@@ -108,13 +111,6 @@
   initialized_ = false;
 }
 
-int ResourceManager::AddDrmDevice(const std::string &path) {
-  auto drm = std::make_unique<DrmDevice>();
-  int ret = drm->Init(path.c_str());
-  drms_.push_back(std::move(drm));
-  return ret;
-}
-
 auto ResourceManager::GetTimeMonotonicNs() -> int64_t {
   struct timespec ts {};
   clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -136,8 +132,10 @@
 
       if (connected) {
         auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);
-        frontend_interface_->BindDisplay(pipeline.get());
-        attached_pipelines_[conn] = std::move(pipeline);
+        if (pipeline) {
+          frontend_interface_->BindDisplay(pipeline.get());
+          attached_pipelines_[conn] = std::move(pipeline);
+        }
       } else {
         auto &pipeline = attached_pipelines_[conn];
         frontend_interface_->UnbindDisplay(pipeline.get());
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index 88ba878..144d00e 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -59,7 +59,6 @@
   static auto GetTimeMonotonicNs() -> int64_t;
 
  private:
-  auto AddDrmDevice(std::string const &path) -> int;
   auto GetOrderedConnectors() -> std::vector<DrmConnector *>;
   void UpdateFrontendDisplays();
   void DetachAllFrontendDisplays();
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index e689419..4accb07 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -177,8 +177,14 @@
         resource_manager_.Init();
       } else {
         resource_manager_.DeInit();
-        /* Headless display may still be here, remove it */
-        displays_.erase(kPrimaryDisplay);
+        /* Headless display may still be here. Remove it! */
+        if (displays_.count(kPrimaryDisplay) != 0) {
+          displays_[kPrimaryDisplay]->Deinit();
+          auto &mutex = GetResMan().GetMainLock();
+          mutex.unlock();
+          displays_.erase(kPrimaryDisplay);
+          mutex.lock();
+        }
       }
       break;
     }
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index cedac19..d968ab3 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -90,6 +90,7 @@
     : hwc2_(hwc2),
       handle_(handle),
       type_(type),
+      client_layer_(this),
       color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) {
   // clang-format off
   color_transform_matrix_ = {1.0, 0.0, 0.0, 0.0,
@@ -102,24 +103,51 @@
 HwcDisplay::~HwcDisplay() = default;
 
 void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) {
+  Deinit();
+
   pipeline_ = pipeline;
 
-  if (pipeline != nullptr) {
-    ChosePreferredConfig();
+  if (pipeline != nullptr || handle_ == kPrimaryDisplay) {
     Init();
-
     hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true);
   } else {
-    backend_.reset();
-    vsync_worker_.Init(nullptr, [](int64_t) {});
-    SetClientTarget(nullptr, -1, 0, {});
-    if (handle_ != kPrimaryDisplay) {
-      hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false);
-    }
+    hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false);
   }
 }
 
+void HwcDisplay::Deinit() {
+  if (pipeline_ != nullptr) {
+    AtomicCommitArgs a_args{};
+    a_args.composition = std::make_shared<DrmKmsPlan>();
+    GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
+/*
+ *  TODO:
+ *  Unfortunately the following causes regressions on db845c
+ *  with VtsHalGraphicsComposerV2_3TargetTest due to the display
+ *  never coming back. Patches to avoiding that issue on the
+ *  the kernel side unfortunately causes further crashes in
+ *  drm_hwcomposer, because the client detach takes longer then the
+ *  1 second max VTS expects. So for now as a workaround, lets skip
+ *  deactivating the display on deinit, which matches previous
+ *  behavior prior to commit d0494d9b8097
+ */
+#if 0
+    a_args.composition = {};
+    a_args.active = false;
+    GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
+#endif
+
+    vsync_worker_.Init(nullptr, [](int64_t) {});
+    current_plan_.reset();
+    backend_.reset();
+  }
+
+  SetClientTarget(nullptr, -1, 0, {});
+}
+
 HWC2::Error HwcDisplay::Init() {
+  ChosePreferredConfig();
+
   int ret = vsync_worker_.Init(pipeline_, [this](int64_t timestamp) {
     const std::lock_guard<std::mutex> lock(hwc2_->GetResMan().GetMainLock());
     if (vsync_event_en_) {
@@ -176,7 +204,7 @@
 }
 
 HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) {
-  layers_.emplace(static_cast<hwc2_layer_t>(layer_idx_), HwcLayer());
+  layers_.emplace(static_cast<hwc2_layer_t>(layer_idx_), HwcLayer(this));
   *layer = static_cast<hwc2_layer_t>(layer_idx_);
   ++layer_idx_;
   return HWC2::Error::None;
@@ -379,6 +407,9 @@
 
 /* Find API details at:
  * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1767
+ *
+ * Called after PresentDisplay(), CLIENT is expecting release fence for the
+ * prior buffer (not the one assigned to the layer at the moment).
  */
 HWC2::Error HwcDisplay::GetReleaseFences(uint32_t *num_elements,
                                          hwc2_layer_t *layers,
@@ -390,8 +421,13 @@
 
   uint32_t num_layers = 0;
 
-  for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
+  for (auto &l : layers_) {
+    if (!l.second.GetPriorBufferScanOutFlag() || !present_fence_) {
+      continue;
+    }
+
     ++num_layers;
+
     if (layers == nullptr || fences == nullptr)
       continue;
 
@@ -401,9 +437,10 @@
     }
 
     layers[num_layers - 1] = l.first;
-    fences[num_layers - 1] = l.second.GetReleaseFence().Release();
+    fences[num_layers - 1] = UniqueFd::Dup(present_fence_.Get()).Release();
   }
   *num_elements = num_layers;
+
   return HWC2::Error::None;
 }
 
@@ -457,18 +494,26 @@
   if (z_map.empty())
     return HWC2::Error::BadLayer;
 
-  std::vector<DrmHwcLayer> composition_layers;
+  std::vector<LayerData> composition_layers;
+
+  /* Import & populate */
+  for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
+    l.second->PopulateLayerData(a_args.test_only);
+  }
 
   // now that they're ordered by z, add them to the composition
   for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
-    DrmHwcLayer layer;
-    l.second->PopulateDrmLayer(&layer);
-    int ret = layer.ImportBuffer(GetPipe().device);
-    if (ret) {
-      ALOGE("Failed to import layer, ret=%d", ret);
-      return HWC2::Error::NoResources;
+    if (!l.second->IsLayerUsableAsDevice()) {
+      /* This will be normally triggered on validation of the first frame
+       * containing CLIENT layer. At this moment client buffer is not yet
+       * provided by the CLIENT.
+       * This may be triggered once in HwcLayer lifecycle in case FB can't be
+       * imported. For example when non-contiguous buffer is imported into
+       * contiguous-only DRM/KMS driver.
+       */
+      return HWC2::Error::BadLayer;
     }
-    composition_layers.emplace_back(std::move(layer));
+    composition_layers.emplace_back(l.second->GetLayerData().Clone());
   }
 
   /* Store plan to ensure shared planes won't be stolen by other display
@@ -508,9 +553,9 @@
 /* Find API details at:
  * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1805
  */
-HWC2::Error HwcDisplay::PresentDisplay(int32_t *present_fence) {
+HWC2::Error HwcDisplay::PresentDisplay(int32_t *out_present_fence) {
   if (IsInHeadlessMode()) {
-    *present_fence = -1;
+    *out_present_fence = -1;
     return HWC2::Error::None;
   }
   HWC2::Error ret{};
@@ -525,13 +570,14 @@
 
   if (ret == HWC2::Error::BadLayer) {
     // Can we really have no client or device layers?
-    *present_fence = -1;
+    *out_present_fence = -1;
     return HWC2::Error::None;
   }
   if (ret != HWC2::Error::None)
     return ret;
 
-  *present_fence = a_args.out_fence.Release();
+  this->present_fence_ = UniqueFd::Dup(a_args.out_fence.Get());
+  *out_present_fence = a_args.out_fence.Release();
 
   ++frame_no_;
   return HWC2::Error::None;
@@ -571,18 +617,25 @@
    * https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h;l=350;drc=944b68180b008456ed2eb4d4d329e33b19bd5166
    */
   if (target == nullptr) {
+    client_layer_.SwChainClearCache();
     return HWC2::Error::None;
   }
 
-  /* TODO: Do not update source_crop every call.
-   * It makes sense to do it once after every hotplug event. */
-  HwcDrmBo bo{};
-  BufferInfoGetter::GetInstance()->ConvertBoInfo(target, &bo);
+  if (IsInHeadlessMode()) {
+    return HWC2::Error::None;
+  }
 
+  client_layer_.PopulateLayerData(/*test = */ true);
+  if (!client_layer_.IsLayerUsableAsDevice()) {
+    ALOGE("Client layer must be always usable by DRM/KMS");
+    return HWC2::Error::BadLayer;
+  }
+
+  auto &bi = client_layer_.GetLayerData().bi;
   hwc_frect_t source_crop = {.left = 0.0F,
                              .top = 0.0F,
-                             .right = static_cast<float>(bo.width),
-                             .bottom = static_cast<float>(bo.height)};
+                             .right = static_cast<float>(bi->width),
+                             .bottom = static_cast<float>(bi->height)};
   client_layer_.SetLayerSourceCrop(source_crop);
 
   return HWC2::Error::None;
@@ -621,11 +674,8 @@
 }
 
 HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) {
-  if (IsInHeadlessMode()) {
-    return HWC2::Error::None;
-  }
-
   auto mode = static_cast<HWC2::PowerMode>(mode_in);
+
   AtomicCommitArgs a_args{};
 
   switch (mode) {
@@ -633,22 +683,30 @@
       a_args.active = false;
       break;
     case HWC2::PowerMode::On:
-      /*
-       * Setting the display to active before we have a composition
-       * can break some drivers, so skip setting a_args.active to
-       * true, as the next composition frame will implicitly activate
-       * the display
-       */
-      return GetPipe().atomic_state_manager->ActivateDisplayUsingDPMS() == 0
-                 ? HWC2::Error::None
-                 : HWC2::Error::BadParameter;
+      a_args.active = true;
       break;
     case HWC2::PowerMode::Doze:
     case HWC2::PowerMode::DozeSuspend:
       return HWC2::Error::Unsupported;
     default:
-      ALOGI("Power mode %d is unsupported\n", mode);
+      ALOGE("Incorrect power mode value (%d)\n", mode);
       return HWC2::Error::BadParameter;
+  }
+
+  if (IsInHeadlessMode()) {
+    return HWC2::Error::None;
+  }
+
+  if (a_args.active) {
+    /*
+     * Setting the display to active before we have a composition
+     * can break some drivers, so skip setting a_args.active to
+     * true, as the next composition frame will implicitly activate
+     * the display
+     */
+    return GetPipe().atomic_state_manager->ActivateDisplayUsingDPMS() == 0
+               ? HWC2::Error::None
+               : HWC2::Error::BadParameter;
   };
 
   int err = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
@@ -673,6 +731,16 @@
     *num_types = *num_requests = 0;
     return HWC2::Error::None;
   }
+
+  /* In current drm_hwc design in case previous frame layer was not validated as
+   * a CLIENT, it is used by display controller (Front buffer). We have to store
+   * this state to provide the CLIENT with the release fences for such buffers.
+   */
+  for (auto &l : layers_) {
+    l.second.SetPriorBufferScanOutFlag(l.second.GetValidatedType() !=
+                                       HWC2::Composition::Client);
+  }
+
   return backend_->ValidateDisplay(this, num_types, num_requests);
 }
 
@@ -782,19 +850,16 @@
                                                      uint32_t *outDataSize,
                                                      uint8_t *outData) {
   if (IsInHeadlessMode()) {
-    return HWC2::Error::None;
+    return HWC2::Error::Unsupported;
   }
+
   auto blob = GetPipe().connector->Get()->GetEdidBlob();
-
-  *outPort = handle_ - 1;
-
   if (!blob) {
-    if (outData == nullptr) {
-      *outDataSize = 0;
-    }
-    return HWC2::Error::None;
+    return HWC2::Error::Unsupported;
   }
 
+  *outPort = handle_; /* TDOD(nobody): What should be here? */
+
   if (outData) {
     *outDataSize = std::min(*outDataSize, blob->length);
     memcpy(outData, blob->data, *outDataSize);
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 98d8e9b..d79efb0 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -22,10 +22,10 @@
 #include <optional>
 
 #include "HwcDisplayConfigs.h"
+#include "compositor/LayerData.h"
 #include "drm/DrmAtomicStateManager.h"
 #include "drm/ResourceManager.h"
 #include "drm/VSyncWorker.h"
-#include "drmhwcomposer.h"
 #include "hwc2_device/HwcLayer.h"
 
 namespace android {
@@ -106,7 +106,7 @@
                                  float *min_luminance);
   HWC2::Error GetReleaseFences(uint32_t *num_elements, hwc2_layer_t *layers,
                                int32_t *fences);
-  HWC2::Error PresentDisplay(int32_t *present_fence);
+  HWC2::Error PresentDisplay(int32_t *out_present_fence);
   HWC2::Error SetActiveConfig(hwc2_config_t config);
   HWC2::Error ChosePreferredConfig();
   HWC2::Error SetClientTarget(buffer_handle_t target, int32_t acquire_fence,
@@ -180,6 +180,8 @@
     return !pipeline_;
   }
 
+  void Deinit();
+
  private:
   enum ClientFlattenningState : int32_t {
     Disabled = -3,
@@ -197,6 +199,8 @@
 
   DrmHwcTwo *const hwc2_;
 
+  UniqueFd present_fence_;
+
   std::optional<DrmMode> staged_mode_;
   int64_t staged_mode_change_time_{};
   uint32_t staged_mode_config_id_{};
diff --git a/hwc2_device/HwcLayer.cpp b/hwc2_device/HwcLayer.cpp
index 66babda..c278732 100644
--- a/hwc2_device/HwcLayer.cpp
+++ b/hwc2_device/HwcLayer.cpp
@@ -18,8 +18,8 @@
 
 #include "HwcLayer.h"
 
-#include <fcntl.h>
-
+#include "HwcDisplay.h"
+#include "bufferinfo/BufferInfoGetter.h"
 #include "utils/log.h"
 
 namespace android {
@@ -32,17 +32,17 @@
 HWC2::Error HwcLayer::SetLayerBlendMode(int32_t mode) {
   switch (static_cast<HWC2::BlendMode>(mode)) {
     case HWC2::BlendMode::None:
-      blending_ = DrmHwcBlending::kNone;
+      blend_mode_ = BufferBlendMode::kNone;
       break;
     case HWC2::BlendMode::Premultiplied:
-      blending_ = DrmHwcBlending::kPreMult;
+      blend_mode_ = BufferBlendMode::kPreMult;
       break;
     case HWC2::BlendMode::Coverage:
-      blending_ = DrmHwcBlending::kCoverage;
+      blend_mode_ = BufferBlendMode::kCoverage;
       break;
     default:
-      ALOGE("Unknown blending mode b=%d", blending_);
-      blending_ = DrmHwcBlending::kNone;
+      ALOGE("Unknown blending mode b=%d", blend_mode_);
+      blend_mode_ = BufferBlendMode::kUndefined;
       break;
   }
   return HWC2::Error::None;
@@ -53,8 +53,10 @@
  */
 HWC2::Error HwcLayer::SetLayerBuffer(buffer_handle_t buffer,
                                      int32_t acquire_fence) {
-  buffer_ = buffer;
   acquire_fence_ = UniqueFd(acquire_fence);
+  buffer_handle_ = buffer;
+  buffer_handle_updated_ = true;
+
   return HWC2::Error::None;
 }
 
@@ -72,54 +74,54 @@
 HWC2::Error HwcLayer::SetLayerDataspace(int32_t dataspace) {
   switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
     case HAL_DATASPACE_STANDARD_BT709:
-      color_space_ = DrmHwcColorSpace::kItuRec709;
+      color_space_ = BufferColorSpace::kItuRec709;
       break;
     case HAL_DATASPACE_STANDARD_BT601_625:
     case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
     case HAL_DATASPACE_STANDARD_BT601_525:
     case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
-      color_space_ = DrmHwcColorSpace::kItuRec601;
+      color_space_ = BufferColorSpace::kItuRec601;
       break;
     case HAL_DATASPACE_STANDARD_BT2020:
     case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
-      color_space_ = DrmHwcColorSpace::kItuRec2020;
+      color_space_ = BufferColorSpace::kItuRec2020;
       break;
     default:
-      color_space_ = DrmHwcColorSpace::kUndefined;
+      color_space_ = BufferColorSpace::kUndefined;
   }
 
   switch (dataspace & HAL_DATASPACE_RANGE_MASK) {
     case HAL_DATASPACE_RANGE_FULL:
-      sample_range_ = DrmHwcSampleRange::kFullRange;
+      sample_range_ = BufferSampleRange::kFullRange;
       break;
     case HAL_DATASPACE_RANGE_LIMITED:
-      sample_range_ = DrmHwcSampleRange::kLimitedRange;
+      sample_range_ = BufferSampleRange::kLimitedRange;
       break;
     default:
-      sample_range_ = DrmHwcSampleRange::kUndefined;
+      sample_range_ = BufferSampleRange::kUndefined;
   }
   return HWC2::Error::None;
 }
 
 HWC2::Error HwcLayer::SetLayerDisplayFrame(hwc_rect_t frame) {
-  display_frame_ = frame;
+  layer_data_.pi.display_frame = frame;
   return HWC2::Error::None;
 }
 
 HWC2::Error HwcLayer::SetLayerPlaneAlpha(float alpha) {
-  alpha_ = alpha;
+  layer_data_.pi.alpha = std::lround(alpha * UINT16_MAX);
   return HWC2::Error::None;
 }
 
 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
 HWC2::Error HwcLayer::SetLayerSidebandStream(
-    const native_handle_t * /*stream*/) {
+    const native_handle_t* /*stream*/) {
   // TODO(nobody): We don't support sideband
   return HWC2::Error::Unsupported;
 }
 
 HWC2::Error HwcLayer::SetLayerSourceCrop(hwc_frect_t crop) {
-  source_crop_ = crop;
+  layer_data_.pi.source_crop = crop;
   return HWC2::Error::None;
 }
 
@@ -137,19 +139,19 @@
   // redundant in this case. 90* rotation can be combined with either horizontal
   // flip or vertical flip, so treat it differently
   if (transform == HWC_TRANSFORM_ROT_270) {
-    l_transform = DrmHwcTransform::kRotate270;
+    l_transform = LayerTransform::kRotate270;
   } else if (transform == HWC_TRANSFORM_ROT_180) {
-    l_transform = DrmHwcTransform::kRotate180;
+    l_transform = LayerTransform::kRotate180;
   } else {
     if ((transform & HWC_TRANSFORM_FLIP_H) != 0)
-      l_transform |= DrmHwcTransform::kFlipH;
+      l_transform |= LayerTransform::kFlipH;
     if ((transform & HWC_TRANSFORM_FLIP_V) != 0)
-      l_transform |= DrmHwcTransform::kFlipV;
+      l_transform |= LayerTransform::kFlipV;
     if ((transform & HWC_TRANSFORM_ROT_90) != 0)
-      l_transform |= DrmHwcTransform::kRotate90;
+      l_transform |= LayerTransform::kRotate90;
   }
 
-  transform_ = static_cast<DrmHwcTransform>(l_transform);
+  layer_data_.pi.transform = static_cast<LayerTransform>(l_transform);
   return HWC2::Error::None;
 }
 
@@ -164,17 +166,129 @@
   return HWC2::Error::None;
 }
 
-void HwcLayer::PopulateDrmLayer(DrmHwcLayer *layer) {
-  layer->sf_handle = buffer_;
-  // TODO(rsglobal): Avoid extra fd duplication
-  layer->acquire_fence = UniqueFd(fcntl(acquire_fence_.Get(), F_DUPFD_CLOEXEC));
-  layer->display_frame = display_frame_;
-  layer->alpha = std::lround(alpha_ * UINT16_MAX);
-  layer->blending = blending_;
-  layer->source_crop = source_crop_;
-  layer->transform = transform_;
-  layer->color_space = color_space_;
-  layer->sample_range = sample_range_;
+void HwcLayer::ImportFb() {
+  if (!IsLayerUsableAsDevice() || !buffer_handle_updated_) {
+    return;
+  }
+  buffer_handle_updated_ = false;
+
+  layer_data_.fb = {};
+
+  auto unique_id = BufferInfoGetter::GetInstance()->GetUniqueId(buffer_handle_);
+  if (unique_id && SwChainGetBufferFromCache(*unique_id)) {
+    return;
+  }
+
+  layer_data_.bi = BufferInfoGetter::GetInstance()->GetBoInfo(buffer_handle_);
+  if (!layer_data_.bi) {
+    ALOGW("Unable to get buffer information (0x%p)", buffer_handle_);
+    bi_get_failed_ = true;
+    return;
+  }
+
+  layer_data_
+      .fb = parent_->GetPipe().device->GetDrmFbImporter().GetOrCreateFbId(
+      &layer_data_.bi.value());
+
+  if (!layer_data_.fb) {
+    ALOGV("Unable to create framebuffer object for buffer 0x%p",
+          buffer_handle_);
+    fb_import_failed_ = true;
+    return;
+  }
+
+  if (unique_id) {
+    SwChainAddCurrentBuffer(*unique_id);
+  }
+}
+
+void HwcLayer::PopulateLayerData(bool test) {
+  ImportFb();
+
+  if (blend_mode_ != BufferBlendMode::kUndefined) {
+    layer_data_.bi->blend_mode = blend_mode_;
+  }
+  if (color_space_ != BufferColorSpace::kUndefined) {
+    layer_data_.bi->color_space = color_space_;
+  }
+  if (sample_range_ != BufferSampleRange::kUndefined) {
+    layer_data_.bi->sample_range = sample_range_;
+  }
+
+  if (!test) {
+    layer_data_.acquire_fence = std::move(acquire_fence_);
+  }
+}
+
+/* SwapChain Cache */
+
+bool HwcLayer::SwChainGetBufferFromCache(BufferUniqueId unique_id) {
+  if (swchain_lookup_table_.count(unique_id) == 0) {
+    return false;
+  }
+
+  int seq = swchain_lookup_table_[unique_id];
+
+  if (swchain_cache_.count(seq) == 0) {
+    return false;
+  }
+
+  auto& el = swchain_cache_[seq];
+  if (!el.bi) {
+    return false;
+  }
+
+  layer_data_.bi = el.bi;
+  layer_data_.fb = el.fb;
+
+  return true;
+}
+
+void HwcLayer::SwChainReassemble(BufferUniqueId unique_id) {
+  if (swchain_lookup_table_.count(unique_id) != 0) {
+    if (swchain_lookup_table_[unique_id] ==
+        int(swchain_lookup_table_.size()) - 1) {
+      /* Skip same buffer */
+      return;
+    }
+    if (swchain_lookup_table_[unique_id] == 0) {
+      swchain_reassembled_ = true;
+      return;
+    }
+    /* Tracking error */
+    SwChainClearCache();
+    return;
+  }
+
+  swchain_lookup_table_[unique_id] = int(swchain_lookup_table_.size());
+}
+
+void HwcLayer::SwChainAddCurrentBuffer(BufferUniqueId unique_id) {
+  if (!swchain_reassembled_) {
+    SwChainReassemble(unique_id);
+  }
+
+  if (swchain_reassembled_) {
+    if (swchain_lookup_table_.count(unique_id) == 0) {
+      SwChainClearCache();
+      return;
+    }
+
+    int seq = swchain_lookup_table_[unique_id];
+
+    if (swchain_cache_.count(seq) == 0) {
+      swchain_cache_[seq] = {};
+    }
+
+    swchain_cache_[seq].bi = layer_data_.bi;
+    swchain_cache_[seq].fb = layer_data_.fb;
+  }
+}
+
+void HwcLayer::SwChainClearCache() {
+  swchain_cache_.clear();
+  swchain_lookup_table_.clear();
+  swchain_reassembled_ = false;
 }
 
 }  // namespace android
\ No newline at end of file
diff --git a/hwc2_device/HwcLayer.h b/hwc2_device/HwcLayer.h
index df4ce6d..41b3dbb 100644
--- a/hwc2_device/HwcLayer.h
+++ b/hwc2_device/HwcLayer.h
@@ -19,14 +19,17 @@
 
 #include <hardware/hwcomposer2.h>
 
-#include <cmath>
-
-#include "drmhwcomposer.h"
+#include "bufferinfo/BufferInfoGetter.h"
+#include "compositor/LayerData.h"
 
 namespace android {
 
+class HwcDisplay;
+
 class HwcLayer {
  public:
+  explicit HwcLayer(HwcDisplay *parent_display) : parent_(parent_display){};
+
   HWC2::Composition GetSfType() const {
     return sf_type_;
   }
@@ -43,35 +46,20 @@
     return sf_type_ != validated_type_;
   }
 
+  bool GetPriorBufferScanOutFlag() const {
+    return prior_buffer_scanout_flag_;
+  }
+
+  void SetPriorBufferScanOutFlag(bool state) {
+    prior_buffer_scanout_flag_ = state;
+  }
+
   uint32_t GetZOrder() const {
     return z_order_;
   }
 
-  buffer_handle_t GetBuffer() {
-    return buffer_;
-  }
-
-  hwc_rect_t GetDisplayFrame() {
-    return display_frame_;
-  }
-
-  UniqueFd GetReleaseFence() {
-    return std::move(release_fence_);
-  }
-
-  void PopulateDrmLayer(DrmHwcLayer *layer);
-
-  bool RequireScalingOrPhasing() const {
-    float src_width = source_crop_.right - source_crop_.left;
-    float src_height = source_crop_.bottom - source_crop_.top;
-
-    auto dest_width = float(display_frame_.right - display_frame_.left);
-    auto dest_height = float(display_frame_.bottom - display_frame_.top);
-
-    bool scaling = src_width != dest_width || src_height != dest_height;
-    bool phasing = (source_crop_.left - std::floor(source_crop_.left) != 0) ||
-                   (source_crop_.top - std::floor(source_crop_.top) != 0);
-    return scaling || phasing;
+  auto &GetLayerData() {
+    return layer_data_;
   }
 
   // Layer hooks
@@ -96,26 +84,59 @@
   HWC2::Composition sf_type_ = HWC2::Composition::Invalid;
   HWC2::Composition validated_type_ = HWC2::Composition::Invalid;
 
-  buffer_handle_t buffer_ = nullptr;
-  hwc_rect_t display_frame_;
-  static constexpr float kOpaqueFloat = 1.0F;
-  float alpha_ = kOpaqueFloat;
-  hwc_frect_t source_crop_;
-  DrmHwcTransform transform_ = DrmHwcTransform::kIdentity;
   uint32_t z_order_ = 0;
-  DrmHwcBlending blending_ = DrmHwcBlending::kNone;
-  DrmHwcColorSpace color_space_ = DrmHwcColorSpace::kUndefined;
-  DrmHwcSampleRange sample_range_ = DrmHwcSampleRange::kUndefined;
+  LayerData layer_data_;
 
+  /* Should be populated to layer_data_.acquire_fence only before presenting */
   UniqueFd acquire_fence_;
 
-  /*
-   * Release fence is not used.
-   * There is no release fence support available in the DRM/KMS. In case no
-   * release fence provided application will use this buffer for writing when
-   * the next frame present fence is signaled.
+  /* The following buffer data can have 2 sources:
+   * 1 - Mapper@4 metadata API
+   * 2 - HWC@2 API
+   * We keep ability to have 2 sources in drm_hwc. It may be useful for CLIENT
+   * layer, at this moment HWC@2 API can't specify blending mode for this layer,
+   * but Mapper@4 can do that
    */
-  UniqueFd release_fence_;
+  BufferColorSpace color_space_{};
+  BufferSampleRange sample_range_{};
+  BufferBlendMode blend_mode_{};
+  buffer_handle_t buffer_handle_{};
+  bool buffer_handle_updated_{};
+
+  bool prior_buffer_scanout_flag_{};
+
+  HwcDisplay *const parent_;
+
+  /* Layer state */
+ public:
+  void PopulateLayerData(bool test);
+
+  bool IsLayerUsableAsDevice() const {
+    return !bi_get_failed_ && !fb_import_failed_ && buffer_handle_ != nullptr;
+  }
+
+ private:
+  void ImportFb();
+  bool bi_get_failed_{};
+  bool fb_import_failed_{};
+
+  /* SwapChain Cache */
+ public:
+  void SwChainClearCache();
+
+ private:
+  struct SwapChainElement {
+    std::optional<BufferInfo> bi;
+    std::shared_ptr<DrmFbIdHandle> fb;
+  };
+
+  bool SwChainGetBufferFromCache(BufferUniqueId unique_id);
+  void SwChainReassemble(BufferUniqueId unique_id);
+  void SwChainAddCurrentBuffer(BufferUniqueId unique_id);
+
+  std::map<int /*seq_no*/, SwapChainElement> swchain_cache_;
+  std::map<BufferUniqueId, int /*seq_no*/> swchain_lookup_table_;
+  bool swchain_reassembled_{};
 };
 
 }  // namespace android
diff --git a/include/drmhwcgralloc.h b/include/drmhwcgralloc.h
deleted file mode 100644
index 949912d..0000000
--- a/include/drmhwcgralloc.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 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_DRMHWCGRALLOC_H_
-#define ANDROID_DRMHWCGRALLOC_H_
-
-#include <cstdint>
-
-constexpr int kHwcDrmBoMaxPlanes = 4;
-
-struct HwcDrmBo {
-  uint32_t width;
-  uint32_t height;
-  uint32_t format;     /* DRM_FORMAT_* from drm_fourcc.h */
-  uint32_t hal_format; /* HAL_PIXEL_FORMAT_* */
-  uint32_t usage;
-  uint32_t pitches[kHwcDrmBoMaxPlanes];
-  uint32_t offsets[kHwcDrmBoMaxPlanes];
-  /* sizes[] is used only by mapper@4 metadata getter for internal purposes */
-  uint32_t sizes[kHwcDrmBoMaxPlanes];
-  int prime_fds[kHwcDrmBoMaxPlanes];
-  uint64_t modifiers[kHwcDrmBoMaxPlanes];
-  int acquire_fence_fd;
-};
-
-// NOLINTNEXTLINE(readability-identifier-naming)
-using hwc_drm_bo_t = HwcDrmBo;
-
-#endif  // ANDROID_DRMHWCGRALLOC_H_
diff --git a/include/drmhwcomposer.h b/include/drmhwcomposer.h
deleted file mode 100644
index 4fb0efd..0000000
--- a/include/drmhwcomposer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 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_DRM_HWCOMPOSER_H_
-#define ANDROID_DRM_HWCOMPOSER_H_
-
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <cstdbool>
-#include <cstdint>
-#include <vector>
-
-#include "drm/DrmFbImporter.h"
-#include "drmhwcgralloc.h"
-#include "utils/UniqueFd.h"
-
-namespace android {
-
-class DrmFbIdHandle;
-
-enum class DrmHwcColorSpace : int32_t {
-  kUndefined,
-  kItuRec601,
-  kItuRec709,
-  kItuRec2020,
-};
-
-enum class DrmHwcSampleRange : int32_t {
-  kUndefined,
-  kFullRange,
-  kLimitedRange,
-};
-
-enum DrmHwcTransform : uint32_t {
-  kIdentity = 0,
-  kFlipH = 1 << 0,
-  kFlipV = 1 << 1,
-  kRotate90 = 1 << 2,
-  kRotate180 = 1 << 3,
-  kRotate270 = 1 << 4,
-};
-
-enum class DrmHwcBlending : int32_t {
-  kNone,
-  kPreMult,
-  kCoverage,
-};
-
-struct DrmHwcLayer {
-  buffer_handle_t sf_handle = nullptr;
-  hwc_drm_bo_t buffer_info{};
-  std::shared_ptr<DrmFbIdHandle> fb_id_handle;
-
-  int gralloc_buffer_usage = 0;
-  DrmHwcTransform transform{};
-  DrmHwcBlending blending = DrmHwcBlending::kNone;
-  uint16_t alpha = UINT16_MAX;
-  hwc_frect_t source_crop;
-  hwc_rect_t display_frame;
-  DrmHwcColorSpace color_space;
-  DrmHwcSampleRange sample_range;
-
-  UniqueFd acquire_fence;
-
-  int ImportBuffer(DrmDevice *drm_device);
-
-  bool IsProtected() const {
-    return (gralloc_buffer_usage & GRALLOC_USAGE_PROTECTED) ==
-           GRALLOC_USAGE_PROTECTED;
-  }
-};
-
-}  // namespace android
-
-#endif
diff --git a/tests/Android.bp b/tests/Android.bp
index b25342e..f3ebb71 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -37,10 +37,7 @@
     header_libs: ["libhardware_headers"],
     static_libs: ["libdrmhwc_utils"],
     shared_libs: ["hwcomposer.drm"],
-    include_dirs: [
-        "external/drm_hwcomposer",
-        "external/drm_hwcomposer/include",
-    ],
+    include_dirs: ["external/drm_hwcomposer"],
 }
 
 // Tool for listening and dumping uevents
diff --git a/utils/UniqueFd.h b/utils/UniqueFd.h
index 1a390ba..d747a7f 100644
--- a/utils/UniqueFd.h
+++ b/utils/UniqueFd.h
@@ -17,6 +17,7 @@
 #ifndef UNIQUEFD_H_
 #define UNIQUEFD_H_
 
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <memory>
@@ -73,6 +74,11 @@
     return fd_;
   }
 
+  static auto Dup(int fd) {
+    // NOLINTNEXTLINE(android-cloexec-dup): fcntl has issue (see issue #63)
+    return UniqueFd(dup(fd));
+  }
+
   explicit operator bool() const {
     return fd_ != kEmptyFd;
   }
diff --git a/utils/Worker.h b/utils/Worker.h
index ab01d5f..74cfdc4 100644
--- a/utils/Worker.h
+++ b/utils/Worker.h
@@ -44,9 +44,10 @@
     return initialized_;
   }
 
+  virtual ~Worker();
+
  protected:
   Worker(const char *name, int priority);
-  virtual ~Worker();
 
   int InitWorker();
   virtual void Routine() = 0;
diff --git a/utils/hwcutils.cpp b/utils/hwcutils.cpp
deleted file mode 100644
index c537b99..0000000
--- a/utils/hwcutils.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#define LOG_TAG "hwc-drm-utils"
-
-#include <utils/log.h>
-
-#include <cerrno>
-
-#include "bufferinfo/BufferInfoGetter.h"
-#include "drm/DrmFbImporter.h"
-#include "drmhwcomposer.h"
-
-namespace android {
-
-int DrmHwcLayer::ImportBuffer(DrmDevice *drm_device) {
-  buffer_info = hwc_drm_bo_t{};
-
-  int ret = BufferInfoGetter::GetInstance()->ConvertBoInfo(sf_handle,
-                                                           &buffer_info);
-  if (ret != 0) {
-    ALOGE("Failed to convert buffer info %d", ret);
-    return ret;
-  }
-
-  fb_id_handle = drm_device->GetDrmFbImporter().GetOrCreateFbId(&buffer_info);
-  if (!fb_id_handle) {
-    ALOGE("Failed to import buffer");
-    return -EINVAL;
-  }
-
-  return 0;
-}
-
-}  // namespace android