Upgrade drm_hwcomposer to 75855a6ab877011a2e26ae95da7ddf06dd3a04b2

This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update external/drm_hwcomposer
For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md

Test: TreeHugger
Change-Id: I33ab547cfa3133dc20fedda280232a2a479b28e6
diff --git a/.ci/.gitlab-ci-checkcommit.sh b/.ci/.gitlab-ci-checkcommit.sh
index 1ca2876..5649475 100755
--- a/.ci/.gitlab-ci-checkcommit.sh
+++ b/.ci/.gitlab-ci-checkcommit.sh
@@ -61,7 +61,7 @@
 		exit 1
 	fi
 
-	git show -U0 "$h" -- | clang-format-diff-19 -p 1 -style=file > /tmp/format-fixup.patch
+	git diff -U0 "$h" -- | clang-format-diff-19 -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/Android.bp b/Android.bp
index 3951949..47b2cd8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,8 +42,6 @@
     name: "hwcomposer.drm_defaults",
 
     shared_libs: [
-        "android.hardware.graphics.composer@2.1-resources",
-        "android.hardware.graphics.composer@2.2-resources",
         "libcutils",
         "libdrm",
         "libhardware",
@@ -130,7 +128,6 @@
     srcs: [
         "hwc3/Composer.cpp",
         "hwc3/ComposerClient.cpp",
-        "hwc3/ComposerResources.cpp",
         "hwc3/DrmHwcThree.cpp",
         "hwc3/Utils.cpp",
     ],
diff --git a/METADATA b/METADATA
index dacef3a..afe2045 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
   last_upgrade_date {
     year: 2025
     month: 2
-    day: 4
+    day: 18
   }
   identifier {
     type: "Git"
     value: "https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer"
-    version: "851ea4dc268c4da612c65a5dfedbd3ed85960405"
+    version: "75855a6ab877011a2e26ae95da7ddf06dd3a04b2"
   }
 }
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index 91cb84d..bf45c08 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -108,7 +108,10 @@
   for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
     if (z_order >= first_z && z_order < first_z + size) {
       auto &df = layers[z_order]->GetLayerData().pi.display_frame;
-      pixops += (df.right - df.left) * (df.bottom - df.top);
+      if (df.i_rect) {
+        pixops += (df.i_rect->right - df.i_rect->left) *
+                  (df.i_rect->bottom - df.i_rect->top);
+      }
     }
   }
   return pixops;
diff --git a/bufferinfo/BufferInfo.h b/bufferinfo/BufferInfo.h
index b2297f9..db4c53e 100644
--- a/bufferinfo/BufferInfo.h
+++ b/bufferinfo/BufferInfo.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <memory>
 
 constexpr int kBufferMaxPlanes = 4;
 
@@ -40,6 +41,11 @@
   kCoverage,
 };
 
+class PrimeFdsSharedBase {
+ public:
+  virtual ~PrimeFdsSharedBase() = default;
+};
+
 struct BufferInfo {
   uint32_t width;
   uint32_t height;
@@ -54,4 +60,9 @@
   BufferColorSpace color_space;
   BufferSampleRange sample_range;
   BufferBlendMode blend_mode;
+
+  /* prime_fds field require valid file descriptors. While their lifecycle is
+   * managed elsewhere. The shared_ptr is used to ensure that the fds are not
+   * closed while the BufferInfo is still in use. */
+  std::shared_ptr<PrimeFdsSharedBase> fds_shared;
 };
diff --git a/compositor/LayerData.h b/compositor/LayerData.h
index 7eb6cba..962141f 100644
--- a/compositor/LayerData.h
+++ b/compositor/LayerData.h
@@ -16,9 +16,6 @@
 
 #pragma once
 
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
 #include <cmath>
 #include <cstdbool>
 #include <cstdint>
@@ -41,22 +38,53 @@
   bool rotate90;
 };
 
+struct SrcRectInfo {
+  struct FRect {
+    float left;
+    float top;
+    float right;
+    float bottom;
+  };
+  /* nullopt means the whole buffer */
+  std::optional<FRect> f_rect;
+};
+
+struct DstRectInfo {
+  struct IRect {
+    int32_t left;
+    int32_t top;
+    int32_t right;
+    int32_t bottom;
+  };
+  /* nullopt means the whole display */
+  std::optional<IRect> i_rect;
+};
+
+constexpr float kAlphaOpaque = 1.0F;
+
 struct PresentInfo {
   LayerTransform transform{};
-  uint16_t alpha = UINT16_MAX;
-  hwc_frect_t source_crop{};
-  hwc_rect_t display_frame{};
+  float alpha = kAlphaOpaque;
+  SrcRectInfo source_crop{};
+  DstRectInfo display_frame{};
 
   bool RequireScalingOrPhasing() const {
-    const float src_width = source_crop.right - source_crop.left;
-    const float src_height = source_crop.bottom - source_crop.top;
+    if (!source_crop.f_rect || !display_frame.i_rect) {
+      return false;
+    }
 
-    auto dest_width = float(display_frame.right - display_frame.left);
-    auto dest_height = float(display_frame.bottom - display_frame.top);
+    const auto &src = *source_crop.f_rect;
+    const auto &dst = *display_frame.i_rect;
+
+    const float src_width = src.right - src.left;
+    const float src_height = src.bottom - src.top;
+
+    auto dest_width = float(dst.right - dst.left);
+    auto dest_height = float(dst.bottom - dst.top);
 
     auto scaling = src_width != dest_width || src_height != dest_height;
-    auto phasing = (source_crop.left - std::floor(source_crop.left) != 0) ||
-                   (source_crop.top - std::floor(source_crop.top) != 0);
+    auto phasing = (src.left - std::floor(src.left) != 0) ||
+                   (src.top - std::floor(src.top) != 0);
     return scaling || phasing;
   }
 };
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 9ce9a93..299d30c 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -120,6 +120,9 @@
       return -EINVAL;
     }
 
+    auto raw_mode = args.display_mode.value().GetRawMode();
+    whole_display_rect_.i_rect = {0, 0, raw_mode.hdisplay, raw_mode.vdisplay};
+
     if (!crtc->GetModeProperty().AtomicSet(*pset, *new_frame_state.mode_blob)) {
       return -EINVAL;
     }
@@ -141,12 +144,14 @@
 
   if (args.colorspace && connector->GetColorspaceProperty()) {
     if (!connector->GetColorspaceProperty()
-             .AtomicSet(*pset, connector->GetColorspacePropertyValue(*args.colorspace)))
+             .AtomicSet(*pset, connector->GetColorspacePropertyValue(
+                                   *args.colorspace)))
       return -EINVAL;
   }
 
   if (args.content_type && connector->GetContentTypeProperty()) {
-    if (!connector->GetContentTypeProperty().AtomicSet(*pset, *args.content_type))
+    if (!connector->GetContentTypeProperty().AtomicSet(*pset,
+                                                       *args.content_type))
       return -EINVAL;
   }
 
@@ -181,8 +186,8 @@
       auto &v = unused_planes;
       v.erase(std::remove(v.begin(), v.end(), joining.plane), v.end());
 
-      if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId()) !=
-          0) {
+      if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId(),
+                                whole_display_rect_) != 0) {
         return -EINVAL;
       }
     }
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 4af04d1..f97a488 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -110,6 +110,8 @@
   int frames_staged_{};
   int frames_tracked_{};
 
+  DstRectInfo whole_display_rect_{};
+
   void ThreadFn(const std::shared_ptr<DrmAtomicStateManager> &dasm);
   std::condition_variable cv_;
   std::mutex mutex_;
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 4534104..6fd5c0c 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -18,6 +18,7 @@
 
 #include "DrmDevice.h"
 
+#include <sys/mman.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -34,13 +35,13 @@
 namespace android {
 
 auto DrmDevice::CreateInstance(std::string const &path,
-                               ResourceManager *res_man)
+                               ResourceManager *res_man, uint32_t index)
     -> std::unique_ptr<DrmDevice> {
   if (!IsKMSDev(path.c_str())) {
     return {};
   }
 
-  auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man));
+  auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man, index));
 
   if (device->Init(path.c_str()) != 0) {
     return {};
@@ -49,7 +50,8 @@
   return device;
 }
 
-DrmDevice::DrmDevice(ResourceManager *res_man) : res_man_(res_man) {
+DrmDevice::DrmDevice(ResourceManager *res_man, uint32_t index)
+    : index_in_dev_array_(index), res_man_(res_man) {
   drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
 }
 
@@ -260,4 +262,93 @@
   return encoders_;
 }
 
+class DumbBufferFd : public PrimeFdsSharedBase {
+ public:
+  SharedFd fd;
+};
+
+// NOLINTBEGIN(cppcoreguidelines-avoid-goto)
+auto DrmDevice::CreateBufferForModeset(uint32_t width, uint32_t height)
+    -> std::optional<BufferInfo> {
+  constexpr uint32_t kDumbBufferFormat = DRM_FORMAT_XRGB8888;
+  constexpr uint32_t kDumbBufferBpp = 32;
+
+  std::optional<BufferInfo> result;
+  void *ptr = MAP_FAILED;
+  struct drm_mode_create_dumb create = {
+      .height = height,
+      .width = width,
+      .bpp = kDumbBufferBpp,
+      .flags = 0,
+  };
+
+  int ret = drmIoctl(*fd_, DRM_IOCTL_MODE_CREATE_DUMB, &create);
+  if (ret != 0) {
+    ALOGE("Failed to DRM_IOCTL_MODE_CREATE_DUMB %d", errno);
+    return {};
+  }
+
+  struct drm_mode_map_dumb map = {
+      .handle = create.handle,
+  };
+
+  auto dumb_buffer_fd = std::make_shared<DumbBufferFd>();
+
+  BufferInfo buffer_info = {
+      .width = width,
+      .height = height,
+
+      .format = kDumbBufferFormat,
+      .pitches = {create.pitch},
+      .prime_fds = {-1, -1, -1, -1},
+      .modifiers = {DRM_FORMAT_MOD_NONE},
+
+      .color_space = BufferColorSpace::kUndefined,
+      .sample_range = BufferSampleRange::kUndefined,
+      .blend_mode = BufferBlendMode::kNone,
+
+      .fds_shared = dumb_buffer_fd,
+  };
+
+  ret = drmIoctl(*fd_, DRM_IOCTL_MODE_MAP_DUMB, &map);
+  if (ret != 0) {
+    ALOGE("Failed to DRM_IOCTL_MODE_MAP_DUMB %d", errno);
+    goto done;
+  }
+
+  ptr = mmap(nullptr, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd_,
+             (off_t)map.offset);
+  if (ptr == MAP_FAILED) {
+    ALOGE("Failed to mmap dumb buffer %d", errno);
+    goto done;
+  }
+
+  memset(ptr, 0, create.size);
+
+  if (munmap(ptr, create.size) != 0) {
+    ALOGE("Failed to unmap dumb buffer: %d", errno);
+  }
+
+  ret = drmPrimeHandleToFD(*fd_, create.handle, 0, &buffer_info.prime_fds[0]);
+  if (ret != 0) {
+    ALOGE("Failed to export dumb buffer as FD: %d", errno);
+    goto done;
+  }
+
+  dumb_buffer_fd->fd = MakeSharedFd(buffer_info.prime_fds[0]);
+
+  result = buffer_info;
+
+done:
+  if (create.handle > 0) {
+    struct drm_mode_destroy_dumb destroy = {
+        .handle = create.handle,
+    };
+    drmIoctl(*fd_, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
+  }
+
+  return result;
+}
+// NOLINTEND(cppcoreguidelines-avoid-goto)
+
 }  // namespace android
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index cbaa536..baa719d 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -18,11 +18,13 @@
 
 #include <cstdint>
 #include <map>
+#include <optional>
 #include <tuple>
 
 #include "DrmConnector.h"
 #include "DrmCrtc.h"
 #include "DrmEncoder.h"
+#include "bufferinfo/BufferInfo.h"
 #include "utils/fd.h"
 
 namespace android {
@@ -35,13 +37,17 @@
  public:
   ~DrmDevice() = default;
 
-  static auto CreateInstance(std::string const &path, ResourceManager *res_man)
-      -> std::unique_ptr<DrmDevice>;
+  static auto CreateInstance(std::string const &path, ResourceManager *res_man,
+                             uint32_t index) -> std::unique_ptr<DrmDevice>;
 
   auto &GetFd() const {
     return fd_;
   }
 
+  auto GetIndexInDevArray() const {
+    return index_in_dev_array_;
+  }
+
   auto &GetResMan() {
     return *res_man_;
   }
@@ -70,6 +76,9 @@
     return HasAddFb2ModifiersSupport_;
   }
 
+  auto CreateBufferForModeset(uint32_t width, uint32_t height)
+      -> std::optional<BufferInfo>;
+
   auto &GetDrmFbImporter() {
     return *drm_fb_importer_;
   }
@@ -98,12 +107,13 @@
                   DrmProperty *property) const;
 
  private:
-  explicit DrmDevice(ResourceManager *res_man);
+  explicit DrmDevice(ResourceManager *res_man, uint32_t index);
   auto Init(const char *path) -> int;
 
   static auto IsKMSDev(const char *path) -> bool;
 
   SharedFd fd_;
+  const uint32_t index_in_dev_array_;
 
   std::vector<std::unique_ptr<DrmConnector>> connectors_;
   std::vector<std::unique_ptr<DrmConnector>> writeback_connectors_;
diff --git a/drm/DrmMode.cpp b/drm/DrmMode.cpp
index 7cbea44..d9fdb37 100644
--- a/drm/DrmMode.cpp
+++ b/drm/DrmMode.cpp
@@ -28,6 +28,11 @@
   return memcmp(&m, &mode_, offsetof(drmModeModeInfo, name)) == 0;
 }
 
+bool DrmMode::SameSize(const DrmMode &mode) const {
+  return (mode_.vdisplay == mode.mode_.vdisplay) &&
+         (mode_.hdisplay == mode.mode_.hdisplay);
+}
+
 auto DrmMode::CreateModeBlob(const DrmDevice &drm)
     -> DrmModeUserPropertyBlobUnique {
   struct drm_mode_modeinfo drm_mode = {};
diff --git a/drm/DrmMode.h b/drm/DrmMode.h
index 5450daf..7520824 100644
--- a/drm/DrmMode.h
+++ b/drm/DrmMode.h
@@ -35,6 +35,8 @@
 
   bool operator==(const drmModeModeInfo &m) const;
 
+  bool SameSize(const DrmMode &mode) const;
+
   auto &GetRawMode() const {
     return mode_;
   }
diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp
index 0010742..68887dd 100644
--- a/drm/DrmPlane.cpp
+++ b/drm/DrmPlane.cpp
@@ -25,6 +25,7 @@
 
 #include "DrmDevice.h"
 #include "bufferinfo/BufferInfoGetter.h"
+#include "compositor/LayerData.h"
 #include "utils/log.h"
 
 namespace android {
@@ -107,17 +108,17 @@
   GetPlaneProperty("IN_FENCE_FD", in_fence_fd_property_, Presence::kOptional);
 
   if (HasNonRgbFormat()) {
-    if (GetPlaneProperty("COLOR_ENCODING", color_encoding_propery_,
+    if (GetPlaneProperty("COLOR_ENCODING", color_encoding_property_,
                          Presence::kOptional)) {
-      color_encoding_propery_.AddEnumToMap("ITU-R BT.709 YCbCr",
-                                           BufferColorSpace::kItuRec709,
-                                           color_encoding_enum_map_);
-      color_encoding_propery_.AddEnumToMap("ITU-R BT.601 YCbCr",
-                                           BufferColorSpace::kItuRec601,
-                                           color_encoding_enum_map_);
-      color_encoding_propery_.AddEnumToMap("ITU-R BT.2020 YCbCr",
-                                           BufferColorSpace::kItuRec2020,
-                                           color_encoding_enum_map_);
+      color_encoding_property_.AddEnumToMap("ITU-R BT.709 YCbCr",
+                                            BufferColorSpace::kItuRec709,
+                                            color_encoding_enum_map_);
+      color_encoding_property_.AddEnumToMap("ITU-R BT.601 YCbCr",
+                                            BufferColorSpace::kItuRec601,
+                                            color_encoding_enum_map_);
+      color_encoding_property_.AddEnumToMap("ITU-R BT.2020 YCbCr",
+                                            BufferColorSpace::kItuRec2020,
+                                            color_encoding_enum_map_);
     }
 
     if (GetPlaneProperty("COLOR_RANGE", color_range_property_,
@@ -145,7 +146,7 @@
     // any CRTC already, which is protected by the plane_switching_crtc function
     // in the kernel drivers/gpu/drm/drm_atomic.c file.
     // The current drm_hwc design is not ready to support such scenario yet,
-    // so adding the CRTC status check here to workaorund for now.
+    // so adding the CRTC status check here to workaround for now.
     return false;
   }
 
@@ -188,7 +189,7 @@
     return false;
   }
 
-  if (!alpha_property_ && layer->pi.alpha != UINT16_MAX) {
+  if (!alpha_property_ && layer->pi.alpha != kAlphaOpaque) {
     ALOGV("Alpha is not supported on plane %d", GetId());
     return false;
   }
@@ -229,7 +230,8 @@
 }
 
 auto DrmPlane::AtomicSetState(drmModeAtomicReq &pset, LayerData &layer,
-                              uint32_t zpos, uint32_t crtc_id) -> int {
+                              uint32_t zpos, uint32_t crtc_id,
+                              DstRectInfo &whole_display_rect) -> int {
   if (!layer.fb || !layer.bi) {
     ALOGE("%s: Invalid arguments", __func__);
     return -EINVAL;
@@ -251,8 +253,24 @@
     return -EINVAL;
   }
 
-  auto &disp = layer.pi.display_frame;
-  auto &src = layer.pi.source_crop;
+  auto opt_disp = layer.pi.display_frame.i_rect;
+  if (!layer.pi.display_frame.i_rect) {
+    opt_disp = whole_display_rect.i_rect;
+  }
+
+  auto opt_src = layer.pi.source_crop.f_rect;
+  if (!layer.pi.source_crop.f_rect) {
+    opt_src = {0.0F, 0.0F, float(layer.bi->width), float(layer.bi->height)};
+  }
+
+  if (!opt_disp || !opt_src) {
+    ALOGE("%s: Invalid display frame or source crop", __func__);
+    return -EINVAL;
+  }
+
+  auto disp = opt_disp.value();
+  auto src = opt_src.value();
+
   if (!crtc_property_.AtomicSet(pset, crtc_id) ||
       !fb_property_.AtomicSet(pset, layer.fb->GetFbId()) ||
       !crtc_x_property_.AtomicSet(pset, disp.left) ||
@@ -271,7 +289,9 @@
     return -EINVAL;
   }
 
-  if (alpha_property_ && !alpha_property_.AtomicSet(pset, layer.pi.alpha)) {
+  if (alpha_property_ &&
+      !alpha_property_.AtomicSet(pset,
+                                 std::lround(layer.pi.alpha * UINT16_MAX))) {
     return -EINVAL;
   }
 
@@ -282,7 +302,7 @@
   }
 
   if (color_encoding_enum_map_.count(layer.bi->color_space) != 0 &&
-      !color_encoding_propery_
+      !color_encoding_property_
            .AtomicSet(pset, color_encoding_enum_map_[layer.bi->color_space])) {
     return -EINVAL;
   }
diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h
index 24d21c8..81306d9 100644
--- a/drm/DrmPlane.h
+++ b/drm/DrmPlane.h
@@ -49,7 +49,7 @@
   bool HasNonRgbFormat() const;
 
   auto AtomicSetState(drmModeAtomicReq &pset, LayerData &layer, uint32_t zpos,
-                      uint32_t crtc_id) -> int;
+                      uint32_t crtc_id, DstRectInfo &whole_display_rect) -> int;
   auto AtomicDisablePlane(drmModeAtomicReq &pset) -> int;
   auto &GetZPosProperty() const {
     return zpos_property_;
@@ -90,7 +90,7 @@
   DrmProperty alpha_property_;
   DrmProperty blend_property_;
   DrmProperty in_fence_fd_property_;
-  DrmProperty color_encoding_propery_;
+  DrmProperty color_encoding_property_;
   DrmProperty color_range_property_;
 
   std::map<BufferBlendMode, uint64_t> blending_enum_map_;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index c866263..fee251e 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -55,7 +55,7 @@
   auto path_len = property_get("vendor.hwc.drm.device", path_pattern,
                                "/dev/dri/card%");
   if (path_pattern[path_len - 1] != '%') {
-    auto dev = DrmDevice::CreateInstance(path_pattern, this);
+    auto dev = DrmDevice::CreateInstance(path_pattern, this, 0);
     if (dev) {
       drms_.emplace_back(std::move(dev));
     }
@@ -69,7 +69,7 @@
       if (stat(path.str().c_str(), &buf) != 0)
         break;
 
-      auto dev = DrmDevice::CreateInstance(path.str(), this);
+      auto dev = DrmDevice::CreateInstance(path.str(), this, idx);
       if (dev) {
         drms_.emplace_back(std::move(dev));
       }
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 16d8bac..42ebd63 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -21,13 +21,7 @@
 
 #include <cinttypes>
 
-#include <xf86drmMode.h>
-
-#include <hardware/gralloc.h>
 #include <ui/ColorSpace.h>
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/GraphicBufferMapper.h>
-#include <ui/PixelFormat.h>
 
 #include "backend/Backend.h"
 #include "backend/BackendManager.h"
@@ -95,98 +89,8 @@
   return color_matrix;
 }
 
-// Allocate a black buffer that can be used for an initial modeset when there.
-// is no appropriate client buffer available to be used.
-// Caller must free the returned buffer with GraphicBufferAllocator::free.
-auto GetModesetBuffer(uint32_t width, uint32_t height) -> buffer_handle_t {
-  constexpr PixelFormat format = PIXEL_FORMAT_RGBA_8888;
-  constexpr uint64_t usage = GRALLOC_USAGE_SW_READ_OFTEN |
-                             GRALLOC_USAGE_SW_WRITE_OFTEN |
-                             GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-
-  constexpr uint32_t layer_count = 1;
-  const std::string name = "drm-hwcomposer";
-
-  buffer_handle_t handle = nullptr;
-  uint32_t stride = 0;
-  status_t status = GraphicBufferAllocator::get().allocate(width, height,
-                                                           format, layer_count,
-                                                           usage, &handle,
-                                                           &stride, name);
-  if (status != OK) {
-    ALOGE("Failed to allocate modeset buffer.");
-    return nullptr;
-  }
-
-  void *data = nullptr;
-  Rect bounds = {0, 0, static_cast<int32_t>(width),
-                 static_cast<int32_t>(height)};
-  status = GraphicBufferMapper::get().lock(handle, usage, bounds, &data);
-  if (status != OK) {
-    ALOGE("Failed to map modeset buffer.");
-    GraphicBufferAllocator::get().free(handle);
-    return nullptr;
-  }
-
-  // Cast one of the multiplicands to ensure that the multiplication happens
-  // in a wider type (size_t).
-  const size_t buffer_size = static_cast<size_t>(height) * stride *
-                             bytesPerPixel(format);
-  memset(data, 0, buffer_size);
-  status = GraphicBufferMapper::get().unlock(handle);
-  ALOGW_IF(status != OK, "Failed to unmap buffer.");
-  return handle;
-}
-
-auto GetModesetLayerProperties(buffer_handle_t buffer, uint32_t width,
-                               uint32_t height) -> HwcLayer::LayerProperties {
-  HwcLayer::LayerProperties properties;
-  properties.buffer = {.buffer_handle = buffer, .acquire_fence = {}};
-  properties.display_frame = {
-      .left = 0,
-      .top = 0,
-      .right = int(width),
-      .bottom = int(height),
-  };
-  properties.source_crop = (hwc_frect_t){
-      .left = 0.0F,
-      .top = 0.0F,
-      .right = static_cast<float>(width),
-      .bottom = static_cast<float>(height),
-  };
-  properties.blend_mode = BufferBlendMode::kNone;
-  return properties;
-}
 }  // namespace
 
-static BufferColorSpace Hwc2ToColorSpace(int32_t dataspace) {
-  switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
-    case HAL_DATASPACE_STANDARD_BT709:
-      return BufferColorSpace::kItuRec709;
-    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:
-      return BufferColorSpace::kItuRec601;
-    case HAL_DATASPACE_STANDARD_BT2020:
-    case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
-      return BufferColorSpace::kItuRec2020;
-    default:
-      return BufferColorSpace::kUndefined;
-  }
-}
-
-static BufferSampleRange Hwc2ToSampleRange(int32_t dataspace) {
-  switch (dataspace & HAL_DATASPACE_RANGE_MASK) {
-    case HAL_DATASPACE_RANGE_FULL:
-      return BufferSampleRange::kFullRange;
-    case HAL_DATASPACE_RANGE_LIMITED:
-      return BufferSampleRange::kLimitedRange;
-    default:
-      return BufferSampleRange::kUndefined;
-  }
-}
-
 std::string HwcDisplay::DumpDelta(HwcDisplay::Stats delta) {
   if (delta.total_pixops_ == 0)
     return "No stats yet";
@@ -305,14 +209,23 @@
     modeset_layer_data = client_layer_.GetLayerData();
   } else {
     ALOGV("Allocate modeset buffer.");
-    buffer_handle_t modeset_buffer = GetModesetBuffer(width, height);
-    if (modeset_buffer != nullptr) {
+    auto modeset_buffer =  //
+        GetPipe().device->CreateBufferForModeset(width, height);
+    if (modeset_buffer) {
       auto modeset_layer = std::make_unique<HwcLayer>(this);
-      modeset_layer->SetLayerProperties(
-          GetModesetLayerProperties(modeset_buffer, width, height));
+      HwcLayer::LayerProperties properties;
+      properties.slot_buffer = {
+          .slot_id = 0,
+          .bi = modeset_buffer,
+      };
+      properties.active_slot = {
+          .slot_id = 0,
+          .fence = {},
+      };
+      properties.blend_mode = BufferBlendMode::kNone;
+      modeset_layer->SetLayerProperties(properties);
       modeset_layer->PopulateLayerData();
       modeset_layer_data = modeset_layer->GetLayerData();
-      GraphicBufferAllocator::get().free(modeset_buffer);
     }
   }
 
@@ -412,12 +325,32 @@
 auto HwcDisplay::PresentStagedComposition(
     SharedFd &out_present_fence, std::vector<ReleaseFence> &out_release_fences)
     -> bool {
-  int out_fd = -1;
-  auto error = PresentDisplay(&out_fd);
-  out_present_fence = MakeSharedFd(out_fd);
-  if (error != HWC2::Error::None) {
-    return false;
+  if (IsInHeadlessMode()) {
+    return true;
   }
+  HWC2::Error ret{};
+
+  ++total_stats_.total_frames_;
+
+  AtomicCommitArgs a_args{};
+  ret = CreateComposition(a_args);
+
+  if (ret != HWC2::Error::None)
+    ++total_stats_.failed_kms_present_;
+
+  if (ret == HWC2::Error::BadLayer) {
+    // Can we really have no client or device layers?
+    return true;
+  }
+  if (ret != HWC2::Error::None)
+    return false;
+
+  out_present_fence = a_args.out_fence;
+
+  // Reset the color matrix so we don't apply it over and over again.
+  color_matrix_ = {};
+
+  ++frame_no_;
 
   if (!out_present_fence) {
     return true;
@@ -467,7 +400,7 @@
     vsync_worker_ = {};
   }
 
-  SetClientTarget(nullptr, -1, 0, {});
+  client_layer_.ClearSlots();
 }
 
 HWC2::Error HwcDisplay::Init() {
@@ -534,12 +467,6 @@
   return SetActiveConfig(configs_.preferred_config_id);
 }
 
-HWC2::Error HwcDisplay::AcceptDisplayChanges() {
-  for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_)
-    l.second.AcceptTypeChange();
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) {
   layers_.emplace(static_cast<hwc2_layer_t>(layer_idx_), HwcLayer(this));
   *layer = static_cast<hwc2_layer_t>(layer_idx_);
@@ -566,53 +493,14 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetChangedCompositionTypes(uint32_t *num_elements,
-                                                   hwc2_layer_t *layers,
-                                                   int32_t *types) {
-  if (IsInHeadlessMode()) {
-    *num_elements = 0;
-    return HWC2::Error::None;
-  }
-
-  uint32_t num_changes = 0;
-  for (auto &l : layers_) {
-    if (l.second.IsTypeChanged()) {
-      if (layers && num_changes < *num_elements)
-        layers[num_changes] = l.first;
-      if (types && num_changes < *num_elements)
-        types[num_changes] = static_cast<int32_t>(l.second.GetValidatedType());
-      ++num_changes;
-    }
-  }
-  if (!layers && !types)
-    *num_elements = num_changes;
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height,
-                                               int32_t /*format*/,
-                                               int32_t dataspace) {
-  if (IsInHeadlessMode()) {
-    return HWC2::Error::None;
-  }
-
-  auto min = pipeline_->device->GetMinResolution();
-  auto max = pipeline_->device->GetMaxResolution();
-
-  if (width < min.first || height < min.second)
-    return HWC2::Error::Unsupported;
-
-  if (width > max.first || height > max.second)
-    return HWC2::Error::Unsupported;
-
-  if (dataspace != HAL_DATASPACE_UNKNOWN)
-    return HWC2::Error::Unsupported;
-
-  // TODO(nobody): Validate format can be handled by either GL or planes
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::GetColorModes(uint32_t *num_modes, int32_t *modes) {
+  if (IsInHeadlessMode()) {
+    *num_modes = 1;
+    if (modes)
+      modes[0] = HAL_COLOR_MODE_NATIVE;
+    return HWC2::Error::None;
+  }
+
   if (!modes) {
     std::vector<Colormode> temp_modes;
     GetEdid()->GetColorModes(temp_modes);
@@ -725,30 +613,20 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDisplayRequests(int32_t * /*display_requests*/,
-                                           uint32_t *num_elements,
-                                           hwc2_layer_t * /*layers*/,
-                                           int32_t * /*layer_requests*/) {
-  // TODO(nobody): I think virtual display should request
-  //      HWC2_DISPLAY_REQUEST_WRITE_CLIENT_TARGET_TO_OUTPUT here
-  *num_elements = 0;
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::GetDisplayType(int32_t *type) {
   *type = static_cast<int32_t>(type_);
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDozeSupport(int32_t *support) {
-  *support = 0;
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types, int32_t *types,
                                            float *max_luminance,
                                            float *max_average_luminance,
                                            float *min_luminance) {
+  if (IsInHeadlessMode()) {
+    *num_types = 0;
+    return HWC2::Error::None;
+  }
+
   if (!types) {
     std::vector<ui::Hdr> temp_types;
     float lums[3] = {0.F};
@@ -777,45 +655,6 @@
   return HWC2::Error::None;
 }
 
-/* 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,
-                                         int32_t *fences) {
-  if (IsInHeadlessMode()) {
-    *num_elements = 0;
-    return HWC2::Error::None;
-  }
-
-  uint32_t num_layers = 0;
-
-  for (auto &l : layers_) {
-    if (!l.second.GetPriorBufferScanOutFlag() || !present_fence_) {
-      continue;
-    }
-
-    ++num_layers;
-
-    if (layers == nullptr || fences == nullptr)
-      continue;
-
-    if (num_layers > *num_elements) {
-      ALOGW("Overflow num_elements %d/%d", num_layers, *num_elements);
-      return HWC2::Error::None;
-    }
-
-    layers[num_layers - 1] = l.first;
-    fences[num_layers - 1] = DupFd(present_fence_);
-  }
-  *num_elements = num_layers;
-
-  return HWC2::Error::None;
-}
-
 AtomicCommitArgs HwcDisplay::CreateModesetCommit(
     const HwcDisplayConfig *config,
     const std::optional<LayerData> &modeset_layer) {
@@ -845,6 +684,7 @@
   return args;
 }
 
+// NOLINTNEXTLINE(readability-function-cognitive-complexity)
 HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
   if (IsInHeadlessMode()) {
     ALOGE("%s: Display is in headless mode, should never reach here", __func__);
@@ -867,14 +707,6 @@
     if (staged_config == nullptr) {
       return HWC2::Error::BadConfig;
     }
-    HwcLayer::LayerProperties lp;
-    lp.display_frame = {
-        .left = 0,
-        .top = 0,
-        .right = int(staged_config->mode.GetRawMode().hdisplay),
-        .bottom = int(staged_config->mode.GetRawMode().vdisplay),
-    };
-    client_layer_.SetLayerProperties(lp);
 
     configs_.active_config_id = staged_mode_config_id_.value();
     a_args.display_mode = staged_config->mode;
@@ -901,9 +733,24 @@
         continue;
     }
   }
-  if (use_client_layer)
+  if (use_client_layer) {
     z_map.emplace(client_z_order, &client_layer_);
 
+    client_layer_.PopulateLayerData();
+    if (!client_layer_.IsLayerUsableAsDevice()) {
+      ALOGE_IF(!a_args.test_only,
+               "Client layer must be always usable by DRM/KMS");
+      /* This may 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;
+    }
+  }
+
   if (z_map.empty())
     return HWC2::Error::BadLayer;
 
@@ -917,13 +764,6 @@
   // now that they're ordered by z, add them to the composition
   for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
     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(l.second->GetLayerData());
@@ -936,6 +776,11 @@
                                                std::move(composition_layers));
 
   if (type_ == HWC2::DisplayType::Virtual) {
+    writeback_layer_->PopulateLayerData();
+    if (!writeback_layer_->IsLayerUsableAsDevice()) {
+      ALOGE("Output layer must be always usable by DRM/KMS");
+      return HWC2::Error::BadLayer;
+    }
     a_args.writeback_fb = writeback_layer_->GetLayerData().fb;
     a_args.writeback_release_fence = writeback_layer_->GetLayerData()
                                          .acquire_fence;
@@ -971,43 +816,6 @@
   return HWC2::Error::None;
 }
 
-/* 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 *out_present_fence) {
-  if (IsInHeadlessMode()) {
-    *out_present_fence = -1;
-    return HWC2::Error::None;
-  }
-  HWC2::Error ret{};
-
-  ++total_stats_.total_frames_;
-
-  AtomicCommitArgs a_args{};
-  ret = CreateComposition(a_args);
-
-  if (ret != HWC2::Error::None)
-    ++total_stats_.failed_kms_present_;
-
-  if (ret == HWC2::Error::BadLayer) {
-    // Can we really have no client or device layers?
-    *out_present_fence = -1;
-    return HWC2::Error::None;
-  }
-  if (ret != HWC2::Error::None)
-    return ret;
-
-  this->present_fence_ = a_args.out_fence;
-  *out_present_fence = DupFd(a_args.out_fence);
-
-  // Reset the color matrix so we don't apply it over and over again.
-  color_matrix_ = {};
-
-  ++frame_no_;
-
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config,
                                                 int64_t change_time) {
   if (configs_.hwc_configs.count(config) == 0) {
@@ -1025,56 +833,6 @@
   return SetActiveConfigInternal(config, ResourceManager::GetTimeMonotonicNs());
 }
 
-/* Find API details at:
- * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1861
- */
-HWC2::Error HwcDisplay::SetClientTarget(buffer_handle_t target,
-                                        int32_t acquire_fence,
-                                        int32_t dataspace,
-                                        hwc_region_t /*damage*/) {
-  HwcLayer::LayerProperties lp;
-  lp.buffer = {.buffer_handle = target,
-               .acquire_fence = MakeSharedFd(acquire_fence)};
-  lp.color_space = Hwc2ToColorSpace(dataspace);
-  lp.sample_range = Hwc2ToSampleRange(dataspace);
-  client_layer_.SetLayerProperties(lp);
-
-  /*
-   * target can be nullptr, this does mean the Composer Service is calling
-   * cleanDisplayResources() on after receiving HOTPLUG event. See more at:
-   * 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;
-  }
-
-  if (IsInHeadlessMode()) {
-    return HWC2::Error::None;
-  }
-
-  client_layer_.PopulateLayerData();
-  if (!client_layer_.IsLayerUsableAsDevice()) {
-    ALOGE("Client layer must be always usable by DRM/KMS");
-    return HWC2::Error::BadLayer;
-  }
-
-  auto &bi = client_layer_.GetLayerData().bi;
-  if (!bi) {
-    ALOGE("%s: Invalid state", __func__);
-    return HWC2::Error::BadLayer;
-  }
-
-  lp = {};
-  lp.source_crop = {.left = 0.0F,
-                    .top = 0.0F,
-                    .right = float(bi->width),
-                    .bottom = float(bi->height)};
-  client_layer_.SetLayerProperties(lp);
-
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetColorMode(int32_t mode) {
   /* Maps to the Colorspace DRM connector property:
    * https://elixir.bootlin.com/linux/v6.11/source/include/drm/drm_connector.h#L538
@@ -1180,21 +938,6 @@
   return true;
 }
 
-HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t buffer,
-                                        int32_t release_fence) {
-  HwcLayer::LayerProperties lp;
-  lp.buffer = {.buffer_handle = buffer,
-               .acquire_fence = MakeSharedFd(release_fence)};
-  writeback_layer_->SetLayerProperties(lp);
-  writeback_layer_->PopulateLayerData();
-  if (!writeback_layer_->IsLayerUsableAsDevice()) {
-    ALOGE("Output layer must be always usable by DRM/KMS");
-    return HWC2::Error::BadLayer;
-  }
-  /* TODO: Check if format is supported by writeback connector */
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) {
   auto mode = static_cast<HWC2::PowerMode>(mode_in);
 
@@ -1261,25 +1004,6 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types,
-                                        uint32_t *num_requests) {
-  if (IsInHeadlessMode()) {
-    *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);
-}
-
 std::vector<HwcLayer *> HwcDisplay::GetOrderLayersByZPos() {
   std::vector<HwcLayer *> ordered_layers;
   ordered_layers.reserve(layers_.size());
@@ -1413,19 +1137,6 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) {
-  return HWC2::Error::Unsupported;
-}
-
-HWC2::Error HwcDisplay::GetSupportedContentTypes(
-    uint32_t *outNumSupportedContentTypes,
-    const uint32_t *outSupportedContentTypes) {
-  if (outSupportedContentTypes == nullptr)
-    *outNumSupportedContentTypes = 0;
-
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetContentType(int32_t contentType) {
   /* Maps exactly to the content_type DRM connector property:
    * https://elixir.bootlin.com/linux/v6.11/source/include/uapi/drm/drm_mode.h#L107
@@ -1447,12 +1158,21 @@
     return HWC2::Error::Unsupported;
   }
 
-  auto blob = GetPipe().connector->Get()->GetEdidBlob();
+  auto *connector = GetPipe().connector->Get();
+  auto blob = connector->GetEdidBlob();
   if (!blob) {
     return HWC2::Error::Unsupported;
   }
 
-  *outPort = handle_; /* TDOD(nobody): What should be here? */
+  constexpr uint8_t kDrmDeviceBitShift = 5U;
+  constexpr uint8_t kDrmDeviceBitMask = 0xE0;
+  constexpr uint8_t kConnectorBitMask = 0x1F;
+  const auto kDrmIdx = static_cast<uint8_t>(
+      connector->GetDev().GetIndexInDevArray());
+  const auto kConnectorIdx = static_cast<uint8_t>(
+      connector->GetIndexInResArray());
+  *outPort = (((kDrmIdx << kDrmDeviceBitShift) & kDrmDeviceBitMask) |
+              (kConnectorIdx & kConnectorBitMask));
 
   if (outData) {
     *outDataSize = std::min(*outDataSize, blob->length);
@@ -1494,15 +1214,6 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDisplayBrightnessSupport(bool *supported) {
-  *supported = false;
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcDisplay::SetDisplayBrightness(float /* brightness */) {
-  return HWC2::Error::Unsupported;
-}
-
 #endif /* __ANDROID_API__ > 28 */
 
 #if __ANDROID_API__ > 27
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 7522c8d..7391785 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -38,6 +38,11 @@
 class Backend;
 class DrmHwc;
 
+class FrontendDisplayBase {
+ public:
+  virtual ~FrontendDisplayBase() = default;
+};
+
 inline constexpr uint32_t kPrimaryDisplay = 0;
 
 // NOLINTNEXTLINE
@@ -108,24 +113,24 @@
                                 std::vector<ReleaseFence> &out_release_fences)
       -> bool;
 
+  auto GetFrontendPrivateData() -> std::shared_ptr<FrontendDisplayBase> {
+    return frontend_private_data_;
+  }
+
+  auto SetFrontendPrivateData(std::shared_ptr<FrontendDisplayBase> data) {
+    frontend_private_data_ = std::move(data);
+  }
+
   // HWC2 Hooks - these should not be used outside of the hwc2 device.
-  HWC2::Error AcceptDisplayChanges();
   HWC2::Error CreateLayer(hwc2_layer_t *layer);
   HWC2::Error DestroyLayer(hwc2_layer_t layer);
   HWC2::Error GetActiveConfig(hwc2_config_t *config) const;
-  HWC2::Error GetChangedCompositionTypes(uint32_t *num_elements,
-                                         hwc2_layer_t *layers, int32_t *types);
-  HWC2::Error GetClientTargetSupport(uint32_t width, uint32_t height,
-                                     int32_t format, int32_t dataspace);
   HWC2::Error GetColorModes(uint32_t *num_modes, int32_t *modes);
   HWC2::Error GetDisplayAttribute(hwc2_config_t config, int32_t attribute,
                                   int32_t *value);
   HWC2::Error LegacyGetDisplayConfigs(uint32_t *num_configs,
                                       hwc2_config_t *configs);
   HWC2::Error GetDisplayName(uint32_t *size, char *name);
-  HWC2::Error GetDisplayRequests(int32_t *display_requests,
-                                 uint32_t *num_elements, hwc2_layer_t *layers,
-                                 int32_t *layer_requests);
   HWC2::Error GetDisplayType(int32_t *type);
 #if __ANDROID_API__ > 27
   HWC2::Error GetRenderIntents(int32_t mode, uint32_t *outNumIntents,
@@ -138,8 +143,6 @@
                                            uint8_t *outData);
   HWC2::Error GetDisplayCapabilities(uint32_t *outNumCapabilities,
                                      uint32_t *outCapabilities);
-  HWC2::Error GetDisplayBrightnessSupport(bool *supported);
-  HWC2::Error SetDisplayBrightness(float);
 #endif
 #if __ANDROID_API__ > 29
   HWC2::Error GetDisplayConnectionType(uint32_t *outType);
@@ -148,33 +151,21 @@
       hwc2_config_t config,
       hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
       hwc_vsync_period_change_timeline_t *outTimeline);
-  HWC2::Error SetAutoLowLatencyMode(bool on);
-  HWC2::Error GetSupportedContentTypes(
-      uint32_t *outNumSupportedContentTypes,
-      const uint32_t *outSupportedContentTypes);
 
   HWC2::Error SetContentType(int32_t contentType);
 #endif
   HWC2::Error GetDisplayVsyncPeriod(uint32_t *outVsyncPeriod);
 
-  HWC2::Error GetDozeSupport(int32_t *support);
   HWC2::Error GetHdrCapabilities(uint32_t *num_types, int32_t *types,
                                  float *max_luminance,
                                  float *max_average_luminance,
                                  float *min_luminance);
-  HWC2::Error GetReleaseFences(uint32_t *num_elements, hwc2_layer_t *layers,
-                               int32_t *fences);
-  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,
-                              int32_t dataspace, hwc_region_t damage);
   HWC2::Error SetColorMode(int32_t mode);
   HWC2::Error SetColorTransform(const float *matrix, int32_t hint);
-  HWC2::Error SetOutputBuffer(buffer_handle_t buffer, int32_t release_fence);
   HWC2::Error SetPowerMode(int32_t mode);
   HWC2::Error SetVsyncEnabled(int32_t enabled);
-  HWC2::Error ValidateDisplay(uint32_t *num_types, uint32_t *num_requests);
   HwcLayer *get_layer(hwc2_layer_t layer) {
     auto it = layers_.find(layer);
     if (it == layers_.end())
@@ -238,6 +229,10 @@
     return flatcon_;
   }
 
+  auto GetClientLayer() -> HwcLayer & {
+    return client_layer_;
+  }
+
   auto &GetWritebackLayer() {
     return writeback_layer_;
   }
@@ -258,8 +253,6 @@
 
   DrmHwc *const hwc_;
 
-  SharedFd present_fence_;
-
   int64_t staged_mode_change_time_{};
   std::optional<uint32_t> staged_mode_config_id_{};
 
@@ -304,6 +297,8 @@
   auto GetEdid() -> EdidWrapperUnique & {
     return GetPipe().connector->Get()->GetParsedEdid();
   }
+
+  std::shared_ptr<FrontendDisplayBase> frontend_private_data_;
 };
 
 }  // namespace android
diff --git a/hwc2_device/HwcLayer.cpp b/hwc2_device/HwcLayer.cpp
index 1d1e118..d0ff216 100644
--- a/hwc2_device/HwcLayer.cpp
+++ b/hwc2_device/HwcLayer.cpp
@@ -25,10 +25,21 @@
 namespace android {
 
 void HwcLayer::SetLayerProperties(const LayerProperties& layer_properties) {
-  if (layer_properties.buffer) {
-    layer_data_.acquire_fence = layer_properties.buffer->acquire_fence;
-    buffer_handle_ = layer_properties.buffer->buffer_handle;
-    buffer_handle_updated_ = true;
+  if (layer_properties.slot_buffer) {
+    auto slot_id = layer_properties.slot_buffer->slot_id;
+    if (!layer_properties.slot_buffer->bi) {
+      slots_.erase(slot_id);
+    } else {
+      slots_[slot_id] = {
+          .bi = layer_properties.slot_buffer->bi.value(),
+          .fb = {},
+      };
+    }
+  }
+  if (layer_properties.active_slot) {
+    active_slot_id_ = layer_properties.active_slot->slot_id;
+    layer_data_.acquire_fence = layer_properties.active_slot->fence;
+    buffer_updated_ = true;
   }
   if (layer_properties.blend_mode) {
     blend_mode_ = layer_properties.blend_mode.value();
@@ -46,8 +57,7 @@
     layer_data_.pi.display_frame = layer_properties.display_frame.value();
   }
   if (layer_properties.alpha) {
-    layer_data_.pi.alpha = std::lround(layer_properties.alpha.value() *
-                                       UINT16_MAX);
+    layer_data_.pi.alpha = layer_properties.alpha.value();
   }
   if (layer_properties.source_crop) {
     layer_data_.pi.source_crop = layer_properties.source_crop.value();
@@ -61,49 +71,39 @@
 }
 
 void HwcLayer::ImportFb() {
-  if (!IsLayerUsableAsDevice() || !buffer_handle_updated_) {
+  if (!IsLayerUsableAsDevice() || !buffer_updated_ ||
+      !active_slot_id_.has_value()) {
     return;
   }
-  buffer_handle_updated_ = false;
+  buffer_updated_ = false;
 
-  layer_data_.fb = {};
-
-  auto unique_id = BufferInfoGetter::GetInstance()->GetUniqueId(buffer_handle_);
-  if (unique_id && SwChainGetBufferFromCache(*unique_id)) {
+  if (slots_[*active_slot_id_].fb) {
     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;
-  }
+  auto& fb_importer = parent_->GetPipe().device->GetDrmFbImporter();
+  auto fb = fb_importer.GetOrCreateFbId(&slots_[*active_slot_id_].bi);
 
-  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_);
+  if (!fb) {
+    ALOGE("Unable to create framebuffer object for layer %p", this);
     fb_import_failed_ = true;
     return;
   }
 
-  if (unique_id) {
-    SwChainAddCurrentBuffer(*unique_id);
-  }
+  slots_[*active_slot_id_].fb = fb;
 }
 
 void HwcLayer::PopulateLayerData() {
   ImportFb();
 
-  if (!layer_data_.bi) {
-    ALOGE("%s: Invalid state", __func__);
+  if (!active_slot_id_.has_value()) {
+    ALOGE("Internal error: populate layer data called without active slot");
     return;
   }
 
+  layer_data_.bi = slots_[*active_slot_id_].bi;
+  layer_data_.fb = slots_[*active_slot_id_].fb;
+
   if (blend_mode_ != BufferBlendMode::kUndefined) {
     layer_data_.bi->blend_mode = blend_mode_;
   }
@@ -115,75 +115,9 @@
   }
 }
 
-/* SwapChain Cache */
-
-bool HwcLayer::SwChainGetBufferFromCache(BufferUniqueId unique_id) {
-  if (swchain_lookup_table_.count(unique_id) == 0) {
-    return false;
-  }
-
-  auto 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;
-    }
-
-    auto 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;
+void HwcLayer::ClearSlots() {
+  slots_.clear();
+  active_slot_id_.reset();
 }
 
 }  // namespace android
\ No newline at end of file
diff --git a/hwc2_device/HwcLayer.h b/hwc2_device/HwcLayer.h
index 93fd18f..c335d88 100644
--- a/hwc2_device/HwcLayer.h
+++ b/hwc2_device/HwcLayer.h
@@ -18,30 +18,43 @@
 
 #include <aidl/android/hardware/graphics/common/Transform.h>
 #include <hardware/hwcomposer2.h>
+#include <memory>
 
+#include "bufferinfo/BufferInfo.h"
 #include "bufferinfo/BufferInfoGetter.h"
 #include "compositor/LayerData.h"
+#include "utils/fd.h"
 
 namespace android {
 
 class HwcDisplay;
 
+class FrontendLayerBase {
+ public:
+  virtual ~FrontendLayerBase() = default;
+};
+
 class HwcLayer {
  public:
   struct Buffer {
-    buffer_handle_t buffer_handle;
-    SharedFd acquire_fence;
+    int32_t slot_id;
+    std::optional<BufferInfo> bi;
+  };
+  struct Slot {
+    int32_t slot_id;
+    SharedFd fence;
   };
   // A set of properties to be validated.
   struct LayerProperties {
-    std::optional<Buffer> buffer;
+    std::optional<Buffer> slot_buffer;
+    std::optional<Slot> active_slot;
     std::optional<BufferBlendMode> blend_mode;
     std::optional<BufferColorSpace> color_space;
     std::optional<BufferSampleRange> sample_range;
     std::optional<HWC2::Composition> composition_type;
-    std::optional<hwc_rect_t> display_frame;
+    std::optional<DstRectInfo> display_frame;
     std::optional<float> alpha;
-    std::optional<hwc_frect_t> source_crop;
+    std::optional<SrcRectInfo> source_crop;
     std::optional<LayerTransform> transform;
     std::optional<uint32_t> z_order;
   };
@@ -82,6 +95,14 @@
 
   void SetLayerProperties(const LayerProperties &layer_properties);
 
+  auto GetFrontendPrivateData() -> std::shared_ptr<FrontendLayerBase> {
+    return frontend_private_data_;
+  }
+
+  auto SetFrontendPrivateData(std::shared_ptr<FrontendLayerBase> data) {
+    frontend_private_data_ = std::move(data);
+  }
+
  private:
   // sf_type_ stores the initial type given to us by surfaceflinger,
   // validated_type_ stores the type after running ValidateDisplay
@@ -101,43 +122,32 @@
   BufferColorSpace color_space_{};
   BufferSampleRange sample_range_{};
   BufferBlendMode blend_mode_{};
-  buffer_handle_t buffer_handle_{};
-  bool buffer_handle_updated_{};
+  bool buffer_updated_{};
 
   bool prior_buffer_scanout_flag_{};
 
   HwcDisplay *const parent_;
 
-  /* Layer state */
- public:
-  void PopulateLayerData();
+  std::shared_ptr<FrontendLayerBase> frontend_private_data_;
 
-  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::optional<int32_t> active_slot_id_;
+  struct BufferSlot {
+    BufferInfo bi;
     std::shared_ptr<DrmFbIdHandle> fb;
   };
+  std::map<int32_t /*slot*/, BufferSlot> slots_;
 
-  bool SwChainGetBufferFromCache(BufferUniqueId unique_id);
-  void SwChainReassemble(BufferUniqueId unique_id);
-  void SwChainAddCurrentBuffer(BufferUniqueId unique_id);
+  void ImportFb();
+  bool fb_import_failed_{};
 
-  std::map<int /*seq_no*/, SwapChainElement> swchain_cache_;
-  std::map<BufferUniqueId, int /*seq_no*/> swchain_lookup_table_;
-  bool swchain_reassembled_{};
+ public:
+  void PopulateLayerData();
+  void ClearSlots();
+
+  bool IsLayerUsableAsDevice() const {
+    return !fb_import_failed_ && active_slot_id_.has_value() &&
+           slots_.count(*active_slot_id_) > 0;
+  }
 };
 
 }  // namespace android
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index 842dd9d..72d0aa9 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -19,10 +19,16 @@
 
 #define LOG_TAG "drmhwc"
 
+#include <cassert>
 #include <cinttypes>
+#include <memory>
+#include <optional>
+
+#include <cutils/native_handle.h>
 
 #include "DrmHwcTwo.h"
 #include "backend/Backend.h"
+#include "hwc2_device/HwcLayer.h"
 #include "utils/log.h"
 
 namespace android {
@@ -43,6 +49,126 @@
   return str.substr(p1, p2 - p1);
 }
 
+class Hwc2DeviceDisplay : public FrontendDisplayBase {
+ public:
+  std::vector<HwcDisplay::ReleaseFence> release_fences;
+  std::vector<HwcDisplay::ChangedLayer> changed_layers;
+};
+
+static auto GetHwc2DeviceDisplay(HwcDisplay &display)
+    -> std::shared_ptr<Hwc2DeviceDisplay> {
+  auto frontend_private_data = display.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc2DeviceDisplay>();
+    display.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc2DeviceDisplay>(frontend_private_data);
+}
+
+class Hwc2DeviceLayer : public FrontendLayerBase {
+ public:
+  auto HandleNextBuffer(buffer_handle_t buffer_handle, int32_t fence_fd)
+      -> std::pair<std::optional<HwcLayer::LayerProperties>,
+                   bool /* not a swapchain */> {
+    auto slot = GetSlotNumber(buffer_handle);
+
+    if (invalid_) {
+      return std::make_pair(std::nullopt, true);
+    }
+
+    bool buffer_provided = false;
+    bool not_a_swapchain = true;
+    int32_t slot_id = 0;
+
+    if (slot.has_value()) {
+      buffer_provided = swchain_slots_[slot.value()];
+      slot_id = slot.value();
+      not_a_swapchain = true;
+    }
+
+    HwcLayer::LayerProperties lp;
+    if (!buffer_provided) {
+      auto bo_info = BufferInfoGetter::GetInstance()->GetBoInfo(buffer_handle);
+      if (!bo_info) {
+        invalid_ = true;
+        return std::make_pair(std::nullopt, true);
+      }
+
+      lp.slot_buffer = {
+          .slot_id = slot_id,
+          .bi = bo_info,
+      };
+    }
+    lp.active_slot = {
+        .slot_id = slot_id,
+        .fence = MakeSharedFd(fence_fd),
+    };
+
+    return std::make_pair(lp, not_a_swapchain);
+  }
+
+  void SwChainClearCache() {
+    swchain_lookup_table_.clear();
+    swchain_slots_.clear();
+    swchain_reassembled_ = false;
+  }
+
+ private:
+  auto GetSlotNumber(buffer_handle_t buffer_handle) -> std::optional<int32_t> {
+    auto unique_id = BufferInfoGetter::GetInstance()->GetUniqueId(
+        buffer_handle);
+    if (!unique_id) {
+      ALOGE("Failed to get unique id for buffer handle %p", buffer_handle);
+      return std::nullopt;
+    }
+
+    if (swchain_lookup_table_.count(*unique_id) == 0) {
+      SwChainReassemble(*unique_id);
+      return std::nullopt;
+    }
+
+    if (!swchain_reassembled_) {
+      return std::nullopt;
+    }
+
+    return swchain_lookup_table_[*unique_id];
+  }
+
+  void 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());
+  }
+
+  bool invalid_{}; /* Layer is invalid and should be skipped */
+  std::map<BufferUniqueId, int /*slot*/> swchain_lookup_table_;
+  std::map<int /*slot*/, bool /*buffer_provided*/> swchain_slots_;
+  bool swchain_reassembled_{};
+};
+
+static auto GetHwc2DeviceLayer(HwcLayer &layer)
+    -> std::shared_ptr<Hwc2DeviceLayer> {
+  auto frontend_private_data = layer.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc2DeviceLayer>();
+    layer.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc2DeviceLayer>(frontend_private_data);
+}
+
 struct Drmhwc2Device : hwc2_device {
   DrmHwcTwo drmhwctwo;
 };
@@ -140,6 +266,257 @@
   }
 }
 
+/* Display functions */
+static int32_t GetDisplayRequests(hwc2_device_t * /*device*/,
+                                  hwc2_display_t /*display*/,
+                                  int32_t * /* out_display_requests */,
+                                  uint32_t *out_num_elements,
+                                  hwc2_layer_t * /*out_layers*/,
+                                  int32_t * /*out_layer_requests*/) {
+  ALOGV("GetDisplayRequests");
+
+  *out_num_elements = 0;
+  return 0;
+}
+
+static int32_t GetDozeSupport(hwc2_device_t * /*device*/,
+                              hwc2_display_t /*display*/,
+                              int32_t *out_support) {
+  ALOGV("GetDozeSupport");
+  *out_support = 0;  // Doze support is not available
+  return 0;
+}
+
+static int32_t GetClientTargetSupport(hwc2_device_t * /*device*/,
+                                      hwc2_display_t /*display*/,
+                                      uint32_t /*width*/, uint32_t /*height*/,
+                                      int32_t /*format*/, int32_t dataspace) {
+  ALOGV("GetClientTargetSupport");
+
+  if (dataspace != HAL_DATASPACE_UNKNOWN)
+    return static_cast<int32_t>(HWC2::Error::Unsupported);
+
+  return 0;
+}
+
+static int32_t SetClientTarget(hwc2_device_t *device, hwc2_display_t display,
+                               buffer_handle_t target, int32_t acquire_fence,
+                               int32_t dataspace, hwc_region_t /*damage*/) {
+  ALOGV("SetClientTarget");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto &client_layer = idisplay->GetClientLayer();
+  auto h2l = GetHwc2DeviceLayer(client_layer);
+  if (!h2l) {
+    client_layer.SetFrontendPrivateData(std::make_shared<Hwc2DeviceLayer>());
+  }
+
+  if (target == nullptr) {
+    client_layer.ClearSlots();
+    h2l->SwChainClearCache();
+
+    return 0;
+  }
+
+  auto [lp, not_a_swapchain] = h2l->HandleNextBuffer(target, acquire_fence);
+  if (!lp) {
+    ALOGE("Failed to process client target");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  if (not_a_swapchain) {
+    client_layer.ClearSlots();
+  }
+
+  lp->color_space = Hwc2ToColorSpace(dataspace);
+  lp->sample_range = Hwc2ToSampleRange(dataspace);
+
+  idisplay->GetClientLayer().SetLayerProperties(lp.value());
+
+  return 0;
+}
+
+static int32_t SetOutputBuffer(hwc2_device_t *device, hwc2_display_t display,
+                               buffer_handle_t buffer, int32_t release_fence) {
+  ALOGV("SetOutputBuffer");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto &writeback_layer = idisplay->GetWritebackLayer();
+  if (!writeback_layer) {
+    ALOGE("Writeback layer is not available");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  auto h2l = GetHwc2DeviceLayer(*writeback_layer);
+  if (!h2l) {
+    writeback_layer->SetFrontendPrivateData(
+        std::make_shared<Hwc2DeviceLayer>());
+  }
+
+  auto [lp, not_a_swapchain] = h2l->HandleNextBuffer(buffer, release_fence);
+  if (!lp) {
+    ALOGE("Failed to process output buffer");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  if (not_a_swapchain) {
+    writeback_layer->ClearSlots();
+  }
+
+  writeback_layer->SetLayerProperties(lp.value());
+
+  return 0;
+}
+
+static int32_t AcceptDisplayChanges(hwc2_device_t *device,
+                                    hwc2_display_t display) {
+  ALOGV("AcceptDisplayChanges");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  idisplay->AcceptValidatedComposition();
+
+  return 0;
+}
+
+static int32_t GetReleaseFences(hwc2_device_t *device, hwc2_display_t display,
+                                uint32_t *out_num_elements,
+                                hwc2_layer_t *out_layers, int32_t *out_fences) {
+  ALOGV("GetReleaseFences");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  if (*out_num_elements < hwc2display->release_fences.size()) {
+    ALOGW("Overflow num_elements %d/%zu", *out_num_elements,
+          hwc2display->release_fences.size());
+    return static_cast<int32_t>(HWC2::Error::NoResources);
+  }
+
+  for (size_t i = 0; i < hwc2display->release_fences.size(); ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_layers[i] = hwc2display->release_fences[i].first;
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_fences[i] = DupFd(hwc2display->release_fences[i].second);
+  }
+
+  *out_num_elements = hwc2display->release_fences.size();
+  hwc2display->release_fences.clear();
+
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+
+static int32_t ValidateDisplay(hwc2_device_t *device, hwc2_display_t display,
+                               uint32_t *out_num_types,
+                               uint32_t *out_num_requests) {
+  ALOGV("ValidateDisplay");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  hwc2display->changed_layers = idisplay->ValidateStagedComposition();
+
+  *out_num_types = hwc2display->changed_layers.size();
+  *out_num_requests = 0;
+
+  return 0;
+}
+
+static int32_t GetChangedCompositionTypes(hwc2_device_t *device,
+                                          hwc2_display_t display,
+                                          uint32_t *out_num_elements,
+                                          hwc2_layer_t *out_layers,
+                                          int32_t *out_types) {
+  ALOGV("GetChangedCompositionTypes");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  if (*out_num_elements < hwc2display->changed_layers.size()) {
+    ALOGW("Overflow num_elements %d/%zu", *out_num_elements,
+          hwc2display->changed_layers.size());
+    return static_cast<int32_t>(HWC2::Error::NoResources);
+  }
+
+  for (size_t i = 0; i < hwc2display->changed_layers.size(); ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_layers[i] = hwc2display->changed_layers[i].first;
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_types[i] = static_cast<int32_t>(hwc2display->changed_layers[i].second);
+  }
+
+  *out_num_elements = hwc2display->changed_layers.size();
+  hwc2display->changed_layers.clear();
+
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+
+static int32_t PresentDisplay(hwc2_device_t *device, hwc2_display_t display,
+                              int32_t *out_release_fence) {
+  ALOGV("PresentDisplay");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  SharedFd out_fence;
+
+  hwc2display->release_fences.clear();
+
+  if (!idisplay->PresentStagedComposition(out_fence,
+                                          hwc2display->release_fences)) {
+    ALOGE("Failed to present display");
+    return static_cast<int32_t>(HWC2::Error::BadDisplay);
+  }
+
+  *out_release_fence = DupFd(out_fence);
+
+  return 0;
+}
+
+#if __ANDROID_API__ >= 28
+
+static int32_t GetDisplayBrightnessSupport(hwc2_device_t * /*device*/,
+                                           hwc2_display_t /*display*/,
+                                           bool *out_support) {
+  ALOGV("GetDisplayBrightnessSupport");
+  *out_support = false;  // Brightness support is not available
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+
+static int32_t SetDisplayBrightness(hwc2_device_t * /*device*/,
+                                    hwc2_display_t /*display*/,
+                                    float /*brightness*/) {
+  ALOGV("SetDisplayBrightness");
+  return static_cast<int32_t>(HWC2::Error::Unsupported);
+}
+
+#endif
+
+#if __ANDROID_API__ >= 29
+static int32_t SetAutoLowLatencyMode(hwc2_device_t * /*device*/,
+                                     hwc2_display_t /*display*/, bool /*on*/) {
+  ALOGV("SetAutoLowLatencyMode");
+  return static_cast<int32_t>(HWC2::Error::Unsupported);
+}
+
+static int32_t GetSupportedContentTypes(
+    hwc2_device_t * /*device*/, hwc2_display_t /*display*/,
+    uint32_t *out_num_supported_content_types,
+    uint32_t * /*out_supported_content_types*/) {
+  ALOGV("GetSupportedContentTypes");
+  *out_num_supported_content_types = 0;
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+#endif
+
+/* Layer functions */
+
 static int32_t SetLayerBlendMode(hwc2_device_t *device, hwc2_display_t display,
                                  hwc2_layer_t layer,
                                  int32_t /*hwc2_blend_mode_t*/ mode) {
@@ -181,10 +558,19 @@
   GET_DISPLAY(display);
   GET_LAYER(layer);
 
-  HwcLayer::LayerProperties layer_properties;
-  layer_properties.buffer = {.buffer_handle = buffer,
-                             .acquire_fence = MakeSharedFd(acquire_fence)};
-  ilayer->SetLayerProperties(layer_properties);
+  auto h2l = GetHwc2DeviceLayer(*ilayer);
+
+  auto [lp, not_a_swapchain] = h2l->HandleNextBuffer(buffer, acquire_fence);
+  if (!lp) {
+    ALOGV("Failed to process layer buffer");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  if (not_a_swapchain) {
+    ilayer->ClearSlots();
+  }
+
+  ilayer->SetLayerProperties(lp.value());
 
   return 0;
 }
@@ -244,7 +630,11 @@
   GET_LAYER(layer);
 
   HwcLayer::LayerProperties layer_properties;
-  layer_properties.display_frame = frame;
+  layer_properties.display_frame = {
+      .i_rect = DstRectInfo::IRect{.left = frame.left,
+                                   .top = frame.top,
+                                   .right = frame.right,
+                                   .bottom = frame.bottom}};
   ilayer->SetLayerProperties(layer_properties);
 
   return 0;
@@ -280,7 +670,11 @@
   GET_LAYER(layer);
 
   HwcLayer::LayerProperties layer_properties;
-  layer_properties.source_crop = crop;
+  layer_properties.source_crop = {
+      .f_rect = SrcRectInfo::FRect{.left = crop.left,
+                                   .top = crop.top,
+                                   .right = crop.right,
+                                   .bottom = crop.bottom}};
   ilayer->SetLayerProperties(layer_properties);
 
   return 0;
@@ -367,9 +761,7 @@
 
     // Display functions
     case HWC2::FunctionDescriptor::AcceptDisplayChanges:
-      return ToHook<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
-          DisplayHook<decltype(&HwcDisplay::AcceptDisplayChanges),
-                      &HwcDisplay::AcceptDisplayChanges>);
+      return (hwc2_function_pointer_t)AcceptDisplayChanges;
     case HWC2::FunctionDescriptor::CreateLayer:
       return ToHook<HWC2_PFN_CREATE_LAYER>(
           DisplayHook<decltype(&HwcDisplay::CreateLayer),
@@ -383,15 +775,9 @@
           DisplayHook<decltype(&HwcDisplay::GetActiveConfig),
                       &HwcDisplay::GetActiveConfig, hwc2_config_t *>);
     case HWC2::FunctionDescriptor::GetChangedCompositionTypes:
-      return ToHook<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
-          DisplayHook<decltype(&HwcDisplay::GetChangedCompositionTypes),
-                      &HwcDisplay::GetChangedCompositionTypes, uint32_t *,
-                      hwc2_layer_t *, int32_t *>);
+      return (hwc2_function_pointer_t)GetChangedCompositionTypes;
     case HWC2::FunctionDescriptor::GetClientTargetSupport:
-      return ToHook<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
-          DisplayHook<decltype(&HwcDisplay::GetClientTargetSupport),
-                      &HwcDisplay::GetClientTargetSupport, uint32_t, uint32_t,
-                      int32_t, int32_t>);
+      return (hwc2_function_pointer_t)GetClientTargetSupport;
     case HWC2::FunctionDescriptor::GetColorModes:
       return ToHook<HWC2_PFN_GET_COLOR_MODES>(
           DisplayHook<decltype(&HwcDisplay::GetColorModes),
@@ -411,41 +797,28 @@
           DisplayHook<decltype(&HwcDisplay::GetDisplayName),
                       &HwcDisplay::GetDisplayName, uint32_t *, char *>);
     case HWC2::FunctionDescriptor::GetDisplayRequests:
-      return ToHook<HWC2_PFN_GET_DISPLAY_REQUESTS>(
-          DisplayHook<decltype(&HwcDisplay::GetDisplayRequests),
-                      &HwcDisplay::GetDisplayRequests, int32_t *, uint32_t *,
-                      hwc2_layer_t *, int32_t *>);
+      return (hwc2_function_pointer_t)GetDisplayRequests;
     case HWC2::FunctionDescriptor::GetDisplayType:
       return ToHook<HWC2_PFN_GET_DISPLAY_TYPE>(
           DisplayHook<decltype(&HwcDisplay::GetDisplayType),
                       &HwcDisplay::GetDisplayType, int32_t *>);
     case HWC2::FunctionDescriptor::GetDozeSupport:
-      return ToHook<HWC2_PFN_GET_DOZE_SUPPORT>(
-          DisplayHook<decltype(&HwcDisplay::GetDozeSupport),
-                      &HwcDisplay::GetDozeSupport, int32_t *>);
+      return (hwc2_function_pointer_t)GetDozeSupport;
     case HWC2::FunctionDescriptor::GetHdrCapabilities:
       return ToHook<HWC2_PFN_GET_HDR_CAPABILITIES>(
           DisplayHook<decltype(&HwcDisplay::GetHdrCapabilities),
                       &HwcDisplay::GetHdrCapabilities, uint32_t *, int32_t *,
                       float *, float *, float *>);
     case HWC2::FunctionDescriptor::GetReleaseFences:
-      return ToHook<HWC2_PFN_GET_RELEASE_FENCES>(
-          DisplayHook<decltype(&HwcDisplay::GetReleaseFences),
-                      &HwcDisplay::GetReleaseFences, uint32_t *, hwc2_layer_t *,
-                      int32_t *>);
+      return (hwc2_function_pointer_t)GetReleaseFences;
     case HWC2::FunctionDescriptor::PresentDisplay:
-      return ToHook<HWC2_PFN_PRESENT_DISPLAY>(
-          DisplayHook<decltype(&HwcDisplay::PresentDisplay),
-                      &HwcDisplay::PresentDisplay, int32_t *>);
+      return (hwc2_function_pointer_t)PresentDisplay;
     case HWC2::FunctionDescriptor::SetActiveConfig:
       return ToHook<HWC2_PFN_SET_ACTIVE_CONFIG>(
           DisplayHook<decltype(&HwcDisplay::SetActiveConfig),
                       &HwcDisplay::SetActiveConfig, hwc2_config_t>);
     case HWC2::FunctionDescriptor::SetClientTarget:
-      return ToHook<HWC2_PFN_SET_CLIENT_TARGET>(
-          DisplayHook<decltype(&HwcDisplay::SetClientTarget),
-                      &HwcDisplay::SetClientTarget, buffer_handle_t, int32_t,
-                      int32_t, hwc_region_t>);
+      return (hwc2_function_pointer_t)SetClientTarget;
     case HWC2::FunctionDescriptor::SetColorMode:
       return ToHook<HWC2_PFN_SET_COLOR_MODE>(
           DisplayHook<decltype(&HwcDisplay::SetColorMode),
@@ -455,9 +828,7 @@
           DisplayHook<decltype(&HwcDisplay::SetColorTransform),
                       &HwcDisplay::SetColorTransform, const float *, int32_t>);
     case HWC2::FunctionDescriptor::SetOutputBuffer:
-      return ToHook<HWC2_PFN_SET_OUTPUT_BUFFER>(
-          DisplayHook<decltype(&HwcDisplay::SetOutputBuffer),
-                      &HwcDisplay::SetOutputBuffer, buffer_handle_t, int32_t>);
+      return (hwc2_function_pointer_t)SetOutputBuffer;
     case HWC2::FunctionDescriptor::SetPowerMode:
       return ToHook<HWC2_PFN_SET_POWER_MODE>(
           DisplayHook<decltype(&HwcDisplay::SetPowerMode),
@@ -467,9 +838,7 @@
           DisplayHook<decltype(&HwcDisplay::SetVsyncEnabled),
                       &HwcDisplay::SetVsyncEnabled, int32_t>);
     case HWC2::FunctionDescriptor::ValidateDisplay:
-      return ToHook<HWC2_PFN_VALIDATE_DISPLAY>(
-          DisplayHook<decltype(&HwcDisplay::ValidateDisplay),
-                      &HwcDisplay::ValidateDisplay, uint32_t *, uint32_t *>);
+      return (hwc2_function_pointer_t)ValidateDisplay;
 #if __ANDROID_API__ > 27
     case HWC2::FunctionDescriptor::GetRenderIntents:
       return ToHook<HWC2_PFN_GET_RENDER_INTENTS>(
@@ -493,13 +862,9 @@
                       &HwcDisplay::GetDisplayCapabilities, uint32_t *,
                       uint32_t *>);
     case HWC2::FunctionDescriptor::GetDisplayBrightnessSupport:
-      return ToHook<HWC2_PFN_GET_DISPLAY_BRIGHTNESS_SUPPORT>(
-          DisplayHook<decltype(&HwcDisplay::GetDisplayBrightnessSupport),
-                      &HwcDisplay::GetDisplayBrightnessSupport, bool *>);
+      return (hwc2_function_pointer_t)GetDisplayBrightnessSupport;
     case HWC2::FunctionDescriptor::SetDisplayBrightness:
-      return ToHook<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>(
-          DisplayHook<decltype(&HwcDisplay::SetDisplayBrightness),
-                      &HwcDisplay::SetDisplayBrightness, float>);
+      return (hwc2_function_pointer_t)SetDisplayBrightness;
 #endif /* __ANDROID_API__ > 28 */
 #if __ANDROID_API__ > 29
     case HWC2::FunctionDescriptor::GetDisplayConnectionType:
@@ -518,14 +883,9 @@
                       hwc2_config_t, hwc_vsync_period_change_constraints_t *,
                       hwc_vsync_period_change_timeline_t *>);
     case HWC2::FunctionDescriptor::SetAutoLowLatencyMode:
-      return ToHook<HWC2_PFN_SET_AUTO_LOW_LATENCY_MODE>(
-          DisplayHook<decltype(&HwcDisplay::SetAutoLowLatencyMode),
-                      &HwcDisplay::SetAutoLowLatencyMode, bool>);
+      return (hwc2_function_pointer_t)SetAutoLowLatencyMode;
     case HWC2::FunctionDescriptor::GetSupportedContentTypes:
-      return ToHook<HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES>(
-          DisplayHook<decltype(&HwcDisplay::GetSupportedContentTypes),
-                      &HwcDisplay::GetSupportedContentTypes, uint32_t *,
-                      uint32_t *>);
+      return (hwc2_function_pointer_t)GetSupportedContentTypes;
     case HWC2::FunctionDescriptor::SetContentType:
       return ToHook<HWC2_PFN_SET_CONTENT_TYPE>(
           DisplayHook<decltype(&HwcDisplay::SetContentType),
diff --git a/hwc3/Composer.cpp b/hwc3/Composer.cpp
index 124380d..28d096c 100644
--- a/hwc3/Composer.cpp
+++ b/hwc3/Composer.cpp
@@ -38,11 +38,13 @@
   }
 
   auto client = ndk::SharedRefBase::make<ComposerClient>();
-  if (!client || !client->Init()) {
+  if (!client) {
     *out_client = nullptr;
     return ToBinderStatus(hwc3::Error::kNoResources);
   }
 
+  client->Init();
+
   *out_client = client;
   client_ = client;
 
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index ff3d42e..50d8ce6 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -40,6 +40,7 @@
 #include <cutils/native_handle.h>
 #include <hardware/hwcomposer2.h>
 #include <hardware/hwcomposer_defs.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include "bufferinfo/BufferInfo.h"
 #include "compositor/DisplayInfo.h"
@@ -49,11 +50,13 @@
 #include "hwc3/DrmHwcThree.h"
 #include "hwc3/Utils.h"
 
+using ::android::DstRectInfo;
 using ::android::HwcDisplay;
 using ::android::HwcDisplayConfig;
 using ::android::HwcDisplayConfigs;
 using ::android::HwcLayer;
 using ::android::LayerTransform;
+using ::android::SrcRectInfo;
 
 #include "utils/log.h"
 
@@ -290,18 +293,29 @@
   return aidl_configuration;
 }
 
-std::optional<hwc_rect> AidlToRect(const std::optional<common::Rect>& rect) {
+std::optional<DstRectInfo> AidlToRect(const std::optional<common::Rect>& rect) {
   if (!rect) {
     return std::nullopt;
   }
-  return hwc_rect{rect->left, rect->top, rect->right, rect->bottom};
+  DstRectInfo dst_rec;
+  dst_rec.i_rect = {.left = rect->left,
+                    .top = rect->top,
+                    .right = rect->right,
+                    .bottom = rect->bottom};
+  return dst_rec;
 }
 
-std::optional<hwc_frect> AidlToFRect(const std::optional<common::FRect>& rect) {
+std::optional<SrcRectInfo> AidlToFRect(
+    const std::optional<common::FRect>& rect) {
   if (!rect) {
     return std::nullopt;
   }
-  return hwc_frect{rect->left, rect->top, rect->right, rect->bottom};
+  SrcRectInfo src_rect;
+  src_rect.f_rect = {.left = rect->left,
+                     .top = rect->top,
+                     .right = rect->right,
+                     .bottom = rect->bottom};
+  return src_rect;
 }
 
 std::optional<float> AidlToAlpha(const std::optional<PlaneAlpha>& alpha) {
@@ -338,17 +352,121 @@
 
 }  // namespace
 
+class Hwc3BufferHandle : public PrimeFdsSharedBase {
+ public:
+  static auto Create(buffer_handle_t handle)
+      -> std::shared_ptr<Hwc3BufferHandle> {
+    auto hwc3 = std::shared_ptr<Hwc3BufferHandle>(new Hwc3BufferHandle());
+
+    ::android::GraphicBufferMapper::get()
+        .importBufferNoValidate(handle, &hwc3->imported_handle_);
+
+    return hwc3;
+  }
+
+  auto GetHandle() const -> buffer_handle_t {
+    return imported_handle_;
+  }
+
+  ~Hwc3BufferHandle() override {
+    ::android::GraphicBufferMapper::get().freeBuffer(imported_handle_);
+  }
+
+ private:
+  Hwc3BufferHandle() = default;
+  buffer_handle_t imported_handle_{};
+};
+
+class Hwc3Layer : public ::android::FrontendLayerBase {
+ public:
+  auto HandleNextBuffer(std::optional<buffer_handle_t> raw_handle,
+                        ::android::SharedFd fence_fd, int32_t slot_id)
+      -> std::optional<HwcLayer::LayerProperties> {
+    HwcLayer::LayerProperties lp;
+    if (!raw_handle && slots_.count(slot_id) != 0) {
+      lp.active_slot = {
+          .slot_id = slot_id,
+          .fence = std::move(fence_fd),
+      };
+
+      return lp;
+    }
+
+    if (!raw_handle) {
+      ALOGE("Buffer handle is nullopt but slot was not cached.");
+      return std::nullopt;
+    }
+
+    auto hwc3 = Hwc3BufferHandle::Create(*raw_handle);
+    if (!hwc3) {
+      return std::nullopt;
+    }
+
+    auto bi = ::android::BufferInfoGetter::GetInstance()->GetBoInfo(
+        hwc3->GetHandle());
+    if (!bi) {
+      return std::nullopt;
+    }
+
+    bi->fds_shared = hwc3;
+
+    lp.slot_buffer = {
+        .slot_id = slot_id,
+        .bi = bi,
+    };
+
+    lp.active_slot = {
+        .slot_id = slot_id,
+        .fence = std::move(fence_fd),
+    };
+
+    slots_[slot_id] = hwc3;
+
+    return lp;
+  }
+
+  [[maybe_unused]]
+  auto HandleClearSlot(int32_t slot_id)
+      -> std::optional<HwcLayer::LayerProperties> {
+    if (slots_.count(slot_id) == 0) {
+      return std::nullopt;
+    }
+
+    slots_.erase(slot_id);
+
+    auto lp = HwcLayer::LayerProperties{};
+    lp.slot_buffer = {
+        .slot_id = slot_id,
+        .bi = std::nullopt,
+    };
+
+    return lp;
+  }
+
+  void ClearSlots() {
+    slots_.clear();
+  }
+
+ private:
+  std::map<int32_t /*slot*/, std::shared_ptr<Hwc3BufferHandle>> slots_;
+};
+
+static auto GetHwc3Layer(HwcLayer& layer) -> std::shared_ptr<Hwc3Layer> {
+  auto frontend_private_data = layer.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc3Layer>();
+    layer.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc3Layer>(frontend_private_data);
+}
+
 ComposerClient::ComposerClient() {
   DEBUG_FUNC();
 }
 
-bool ComposerClient::Init() {
+void ComposerClient::Init() {
   DEBUG_FUNC();
-  composer_resources_ = ComposerResources::Create();
-  if (composer_resources_) {
-    hwc_ = std::make_unique<DrmHwcThree>(composer_resources_.get());
-  }
-  return composer_resources_ != nullptr;
+  hwc_ = std::make_unique<DrmHwcThree>();
 }
 
 ComposerClient::~ComposerClient() {
@@ -362,7 +480,7 @@
 }
 
 ndk::ScopedAStatus ComposerClient::createLayer(int64_t display_id,
-                                               int32_t buffer_slot_count,
+                                               int32_t /*buffer_slot_count*/,
                                                int64_t* layer_id) {
   DEBUG_FUNC();
   const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
@@ -378,21 +496,13 @@
     return ToBinderStatus(err);
   }
 
-  const int64_t created_layer_id = Hwc2LayerToHwc3(hwc2_layer_id);
-  err = composer_resources_->AddLayer(display_id, created_layer_id,
-                                      buffer_slot_count);
-  if (err != hwc3::Error::kNone) {
-    destroyLayer(display_id, created_layer_id);
-    return ToBinderStatus(err);
-  }
-
-  *layer_id = created_layer_id;
+  *layer_id = Hwc2LayerToHwc3(hwc2_layer_id);
   return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus ComposerClient::createVirtualDisplay(
     int32_t width, int32_t height, AidlPixelFormat format_hint,
-    int32_t output_buffer_slot_count, VirtualDisplay* out_display) {
+    int32_t /*output_buffer_slot_count*/, VirtualDisplay* out_display) {
   DEBUG_FUNC();
   const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
 
@@ -406,15 +516,7 @@
     return ToBinderStatus(err);
   }
 
-  const int64_t created_display_id = Hwc2DisplayToHwc3(hwc2_display_id);
-  err = composer_resources_->AddVirtualDisplay(hwc2_display_id,
-                                               output_buffer_slot_count);
-  if (err != hwc3::Error::kNone) {
-    hwc_->DestroyVirtualDisplay(hwc2_display_id);
-    return ToBinderStatus(err);
-  }
-
-  out_display->display = created_display_id;
+  out_display->display = Hwc2DisplayToHwc3(hwc2_display_id);
   out_display->format = format_hint;
   return ndk::ScopedAStatus::ok();
 }
@@ -433,7 +535,6 @@
     return ToBinderStatus(err);
   }
 
-  err = composer_resources_->RemoveLayer(display_id, layer_id);
   return ToBinderStatus(err);
 }
 
@@ -476,16 +577,44 @@
     return;
   }
 
+#if __ANDROID_API__ >= 34
+  /* https://source.android.com/docs/core/graphics/reduce-consumption */
+  if (command.bufferSlotsToClear) {
+    auto hwc3_layer = GetHwc3Layer(*layer);
+    for (const auto& slot : *command.bufferSlotsToClear) {
+      auto lp = hwc3_layer->HandleClearSlot(slot);
+      if (!lp) {
+        cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
+        return;
+      }
+
+      layer->SetLayerProperties(lp.value());
+    }
+  }
+#endif
+
   HwcLayer::LayerProperties properties;
   if (command.buffer) {
-    HwcLayer::Buffer buffer;
-    auto err = ImportLayerBuffer(display_id, command.layer, *command.buffer,
-                                 &buffer);
-    if (err != hwc3::Error::kNone) {
-      cmd_result_writer_->AddError(err);
+    auto hwc3_layer = GetHwc3Layer(*layer);
+    std::optional<buffer_handle_t> buffer_handle = std::nullopt;
+    if (command.buffer->handle) {
+      buffer_handle = ::android::makeFromAidl(*command.buffer->handle);
+    }
+
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+    auto fence = const_cast<::ndk::ScopedFileDescriptor&>(command.buffer->fence)
+                     .release();
+
+    auto lp = hwc3_layer->HandleNextBuffer(buffer_handle,
+                                           ::android::MakeSharedFd(fence),
+                                           command.buffer->slot);
+
+    if (!lp) {
+      cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
       return;
     }
-    properties.buffer.emplace(buffer);
+
+    properties = lp.value();
   }
 
   properties.blend_mode = AidlToBlendMode(command.blendMode);
@@ -571,7 +700,8 @@
                                             composition_type));
     }
     cmd_result_writer_->AddChanges(changes);
-    composer_resources_->SetDisplayMustValidateState(display_id, false);
+    auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
+    hwc3_display->must_validate = false;
 
     // TODO: DisplayRequests are not implemented.
 
@@ -585,11 +715,12 @@
   }
 
   if (command.acceptDisplayChanges) {
-    display->AcceptDisplayChanges();
+    display->AcceptValidatedComposition();
   }
 
   if (command.presentDisplay) {
-    if (composer_resources_->MustValidateDisplay(display_id)) {
+    auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
+    if (hwc3_display->must_validate) {
       cmd_result_writer_->AddError(hwc3::Error::kNotValidated);
       return;
     }
@@ -1089,6 +1220,20 @@
                                  next_config != nullptr &&
                                  current_config->group_id ==
                                      next_config->group_id;
+  const bool same_resolution = current_config != nullptr &&
+                               next_config != nullptr &&
+                               current_config->mode.SameSize(next_config->mode);
+
+  /* Client framebuffer management:
+   * https://source.android.com/docs/core/graphics/framebuffer-mgmt
+   */
+  if (!same_resolution && !future_config) {
+    auto& client_layer = display->GetClientLayer();
+    auto hwc3_layer = GetHwc3Layer(client_layer);
+    hwc3_layer->ClearSlots();
+    client_layer.ClearSlots();
+  }
+
   // If the contraints dictate that this is to be applied in the future, it
   // must be queued. If the new config is in the same config group as the
   // current one, then queue it to reduce jank.
@@ -1139,7 +1284,7 @@
 }
 
 ndk::ScopedAStatus ComposerClient::setAutoLowLatencyMode(int64_t display_id,
-                                                         bool on) {
+                                                         bool /*on*/) {
   DEBUG_FUNC();
   const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
   HwcDisplay* display = GetDisplay(display_id);
@@ -1147,15 +1292,13 @@
     return ToBinderStatus(hwc3::Error::kBadDisplay);
   }
 
-  auto error = Hwc2toHwc3Error(display->SetAutoLowLatencyMode(on));
-  return ToBinderStatus(error);
+  return ToBinderStatus(hwc3::Error::kUnsupported);
 }
 
-ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(int64_t display_id,
-                                                            int32_t count) {
+ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(
+    int64_t /*display_id*/, int32_t /*count*/) {
   DEBUG_FUNC();
-  return ToBinderStatus(
-      composer_resources_->SetDisplayClientTargetCacheSize(display_id, count));
+  return ToBinderStatus(hwc3::Error::kNone);
 }
 
 ndk::ScopedAStatus ComposerClient::setColorMode(int64_t display_id,
@@ -1284,7 +1427,8 @@
 }
 
 ndk::ScopedAStatus ComposerClient::notifyExpectedPresent(
-    int64_t /*display*/, const ClockMonotonicTimestamp& /*expected_present_time*/,
+    int64_t /*display*/,
+    const ClockMonotonicTimestamp& /*expected_present_time*/,
     int32_t /*frame_interval_ns*/) {
   return ToBinderStatus(hwc3::Error::kUnsupported);
 }
@@ -1306,19 +1450,6 @@
   return binder;
 }
 
-hwc3::Error ComposerClient::ImportLayerBuffer(int64_t display_id,
-                                              int64_t layer_id,
-                                              const Buffer& buffer,
-                                              HwcLayer::Buffer* out_buffer) {
-  auto releaser = ComposerResources::CreateResourceReleaser(true);
-  auto err = composer_resources_->GetLayerBuffer(display_id, layer_id, buffer,
-                                                 &out_buffer->buffer_handle,
-                                                 releaser.get());
-  out_buffer->acquire_fence = ::android::MakeSharedFd(
-      buffer.fence.dup().release());
-  return err;
-}
-
 void ComposerClient::ExecuteSetDisplayClientTarget(
     uint64_t display_id, const ClientTarget& command) {
   auto* display = GetDisplay(display_id);
@@ -1327,37 +1458,35 @@
     return;
   }
 
-  hwc_region_t damage_regions;
-  damage_regions.numRects = command.damage.size();
+  auto& client_layer = display->GetClientLayer();
+  auto hwc3layer = GetHwc3Layer(client_layer);
 
-  std::vector<hwc_rect_t> regions(command.damage.size());
-  for (const auto& region : command.damage) {
-    regions.push_back({region.left, region.top, region.right, region.bottom});
-  }
-  damage_regions.rects = regions.data();
-
-  buffer_handle_t imported_buffer = nullptr;
-  auto buf_releaser = ComposerResources::CreateResourceReleaser(true);
-
-  auto error = composer_resources_->GetDisplayClientTarget(display_id,
-                                                           command.buffer,
-                                                           &imported_buffer,
-                                                           buf_releaser.get());
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-    return;
+  std::optional<buffer_handle_t> raw_buffer = std::nullopt;
+  if (command.buffer.handle) {
+    raw_buffer = ::android::makeFromAidl(*command.buffer.handle);
   }
 
   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
   auto fence = const_cast<::ndk::ScopedFileDescriptor&>(command.buffer.fence)
                    .release();
-  error = Hwc2toHwc3Error(
-      display->SetClientTarget(imported_buffer, fence,
-                               Hwc3DataspaceToHwc2(command.dataspace),
-                               damage_regions));
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
+
+  auto properties = hwc3layer->HandleNextBuffer(raw_buffer,
+                                                ::android::MakeSharedFd(fence),
+                                                command.buffer.slot);
+
+  if (!properties) {
+    ALOGE("Failed to import client target buffer.");
+    /* Here, sending an error would be the natural way to do the thing.
+     * But VTS checks for no error. Is it the VTS issue?
+     * https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp;l=1892;drc=2647200f4c535ca6567b452695b7d13f2aaf3f2a
+     */
+    return;
   }
+
+  properties->color_space = AidlToColorSpace(command.dataspace);
+  properties->sample_range = AidlToSampleRange(command.dataspace);
+
+  client_layer.SetLayerProperties(properties.value());
 }
 
 void ComposerClient::ExecuteSetDisplayOutputBuffer(uint64_t display_id,
@@ -1368,24 +1497,32 @@
     return;
   }
 
-  buffer_handle_t imported_buffer = nullptr;
-  auto buf_releaser = ComposerResources::CreateResourceReleaser(true);
-
-  auto error = composer_resources_->GetDisplayOutputBuffer(display_id, buffer,
-                                                           &imported_buffer,
-                                                           buf_releaser.get());
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
+  auto& writeback_layer = display->GetWritebackLayer();
+  if (!writeback_layer) {
+    cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
     return;
   }
 
+  auto hwc3layer = GetHwc3Layer(*writeback_layer);
+
+  std::optional<buffer_handle_t> raw_buffer = std::nullopt;
+  if (buffer.handle) {
+    raw_buffer = ::android::makeFromAidl(*buffer.handle);
+  }
+
   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
   auto fence = const_cast<::ndk::ScopedFileDescriptor&>(buffer.fence).release();
-  error = Hwc2toHwc3Error(display->SetOutputBuffer(imported_buffer, fence));
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
+
+  auto properties = hwc3layer->HandleNextBuffer(raw_buffer,
+                                                ::android::MakeSharedFd(fence),
+                                                buffer.slot);
+
+  if (!properties) {
+    cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
     return;
   }
+
+  writeback_layer->SetLayerProperties(properties.value());
 }
 
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/hwc3/ComposerClient.h b/hwc3/ComposerClient.h
index 37cb8ab..25dc0ca 100644
--- a/hwc3/ComposerClient.h
+++ b/hwc3/ComposerClient.h
@@ -22,7 +22,6 @@
 #include "aidl/android/hardware/graphics/composer3/LayerCommand.h"
 #include "hwc2_device/HwcLayer.h"
 #include "hwc3/CommandResultWriter.h"
-#include "hwc3/ComposerResources.h"
 #include "hwc3/Utils.h"
 #include "utils/Mutex.h"
 
@@ -42,7 +41,7 @@
   ComposerClient();
   ~ComposerClient() override;
 
-  bool Init();
+  void Init();
   std::string Dump();
 
   // composer3 interface
@@ -176,9 +175,6 @@
 
   std::unique_ptr<CommandResultWriter> cmd_result_writer_;
 
-  // Manages importing and caching gralloc buffers for displays and layers.
-  std::unique_ptr<ComposerResources> composer_resources_;
-
   std::unique_ptr<DrmHwcThree> hwc_;
 };
 
diff --git a/hwc3/ComposerResources.cpp b/hwc3/ComposerResources.cpp
deleted file mode 100644
index 5e19082..0000000
--- a/hwc3/ComposerResources.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-
-/*
- * Copyright (C) 2024 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 LOG_TAG "drmhwc"
-#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
-
-#include "ComposerResources.h"
-
-#include <aidlcommonsupport/NativeHandle.h>
-
-#include "hardware/hwcomposer2.h"
-#include "hwc3/Utils.h"
-
-namespace {
-using Hwc2Display = ::android::hardware::graphics::composer::V2_1::Display;
-using Hwc2Layer = ::android::hardware::graphics::composer::V2_1::Layer;
-
-auto ToHwc2Display(uint64_t display_id) -> Hwc2Display {
-  return static_cast<Hwc2Display>(display_id);
-}
-
-auto ToHwc2Layer(int64_t layer_id) -> Hwc2Layer {
-  return static_cast<Hwc2Layer>(layer_id);
-}
-}  // namespace
-
-namespace aidl::android::hardware::graphics::composer3::impl {
-
-std::unique_ptr<ComposerResourceReleaser>
-ComposerResources::CreateResourceReleaser(bool is_buffer) {
-  return std::make_unique<ComposerResourceReleaser>(is_buffer);
-}
-
-std::unique_ptr<ComposerResources> ComposerResources::Create() {
-  auto instance = std::unique_ptr<ComposerResources>(new ComposerResources);
-  if (instance->resources_ == nullptr) {
-    ALOGE("%s: Failed to initialise ComposerResources", __func__);
-    return nullptr;
-  }
-
-  return instance;
-}
-
-hwc3::Error ComposerResources::GetLayerBuffer(
-    uint64_t display_id, int64_t layer_id, const Buffer& buffer,
-    buffer_handle_t* out_buffer_handle,
-    ComposerResourceReleaser* buf_releaser) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  const bool use_cache = !buffer.handle.has_value();
-  buffer_handle_t buffer_handle = nullptr;
-  if (buffer.handle.has_value()) {
-    buffer_handle = ::android::makeFromAidl(*buffer.handle);
-  }
-
-  auto err = resources_->getLayerBuffer(display, layer, buffer.slot, use_cache,
-                                        buffer_handle, out_buffer_handle,
-                                        buf_releaser->GetReplacedHandle());
-
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetLayerSidebandStream(
-    uint64_t display_id, int64_t layer_id,
-    const aidl::android::hardware::common::NativeHandle& handle,
-    buffer_handle_t* out_handle, ComposerResourceReleaser* releaser) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  auto err = resources_->getLayerSidebandStream(display, layer,
-                                                ::android::makeFromAidl(handle),
-                                                out_handle,
-                                                releaser->GetReplacedHandle());
-
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::AddLayer(uint64_t display_id, int64_t layer_id,
-                                        uint32_t buffer_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  auto err = resources_->addLayer(display, layer, buffer_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::RemoveLayer(uint64_t display_id,
-                                           int64_t layer_id) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  auto err = resources_->removeLayer(display, layer);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-bool ComposerResources::HasDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  return resources_->hasDisplay(display);
-}
-
-hwc3::Error ComposerResources::AddPhysicalDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->addPhysicalDisplay(display);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::AddVirtualDisplay(
-    uint64_t display, uint32_t output_buffer_cache_size) {
-  auto err = resources_->addVirtualDisplay(display, output_buffer_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::RemoveDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->removeDisplay(display);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-void ComposerResources::SetDisplayMustValidateState(uint64_t display_id,
-                                                    bool must_validate) {
-  auto display = ToHwc2Display(display_id);
-  resources_->setDisplayMustValidateState(display, must_validate);
-}
-
-bool ComposerResources::MustValidateDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  return resources_->mustValidateDisplay(display);
-}
-
-hwc3::Error ComposerResources::GetDisplayClientTarget(
-    uint64_t display_id, const Buffer& buffer, buffer_handle_t* out_handle,
-    ComposerResourceReleaser* releaser) {
-  auto display = ToHwc2Display(display_id);
-
-  const bool use_cache = !buffer.handle.has_value();
-  buffer_handle_t buffer_handle = nullptr;
-  if (buffer.handle.has_value()) {
-    buffer_handle = ::android::makeFromAidl(*buffer.handle);
-  }
-
-  auto err = resources_->getDisplayClientTarget(display, buffer.slot, use_cache,
-                                                buffer_handle, out_handle,
-                                                releaser->GetReplacedHandle());
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::SetDisplayClientTargetCacheSize(
-    uint64_t display_id, uint32_t client_target_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_
-                 ->setDisplayClientTargetCacheSize(display,
-                                                   client_target_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetDisplayClientTargetCacheSize(
-    uint64_t display_id, size_t* out_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->getDisplayClientTargetCacheSize(display,
-                                                         out_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetDisplayOutputBufferCacheSize(
-    uint64_t display_id, size_t* out_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->getDisplayOutputBufferCacheSize(display,
-                                                         out_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetDisplayOutputBuffer(
-    uint64_t display_id, const Buffer& buffer, buffer_handle_t* out_handle,
-    ComposerResourceReleaser* releaser) {
-  auto display = ToHwc2Display(display_id);
-  const bool use_cache = !buffer.handle.has_value();
-
-  buffer_handle_t buffer_handle = nullptr;
-  if (buffer.handle.has_value()) {
-    buffer_handle = ::android::makeFromAidl(*buffer.handle);
-  }
-
-  auto err = resources_->getDisplayOutputBuffer(display, buffer.slot, use_cache,
-                                                buffer_handle, out_handle,
-                                                releaser->GetReplacedHandle());
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/hwc3/ComposerResources.h b/hwc3/ComposerResources.h
deleted file mode 100644
index 6f4eee7..0000000
--- a/hwc3/ComposerResources.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2024 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 <memory>
-
-#include "aidl/android/hardware/graphics/composer3/IComposerClient.h"
-#include "composer-resources/2.2/ComposerResources.h"
-#include "cutils/native_handle.h"
-#include "hwc3/Utils.h"
-
-namespace aidl::android::hardware::graphics::composer3::impl {
-
-class ComposerResourceReleaser {
- public:
-  explicit ComposerResourceReleaser(bool is_buffer)
-      : replaced_handle_(is_buffer) {
-  }
-  virtual ~ComposerResourceReleaser() = default;
-
-  ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
-      ReplacedHandle*
-      GetReplacedHandle() {
-    return &replaced_handle_;
-  }
-
- private:
-  ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
-      ReplacedHandle replaced_handle_;
-};
-
-class ComposerResources {
- public:
-  static std::unique_ptr<ComposerResources> Create();
-  ~ComposerResources() = default;
-
-  hwc3::Error GetLayerBuffer(uint64_t display_id, int64_t layer_id,
-                             const Buffer& buffer,
-                             buffer_handle_t* out_buffer_handle,
-                             ComposerResourceReleaser* releaser);
-  hwc3::Error GetLayerSidebandStream(
-      uint64_t display_id, int64_t layer_id,
-      const aidl::android::hardware::common::NativeHandle& handle,
-      buffer_handle_t* out_handle, ComposerResourceReleaser* releaser);
-
-  hwc3::Error AddLayer(uint64_t display, int64_t layer,
-                       uint32_t buffer_cache_size);
-  hwc3::Error RemoveLayer(uint64_t display, int64_t layer);
-
-  bool HasDisplay(uint64_t display);
-  hwc3::Error AddPhysicalDisplay(uint64_t display);
-  hwc3::Error AddVirtualDisplay(uint64_t display,
-                                uint32_t output_buffer_cache_size);
-  hwc3::Error RemoveDisplay(uint64_t display);
-
-  void SetDisplayMustValidateState(uint64_t display_id, bool must_validate);
-  bool MustValidateDisplay(uint64_t display_id);
-
-  hwc3::Error GetDisplayClientTarget(uint64_t display_id, const Buffer& buffer,
-                                     buffer_handle_t* out_handle,
-                                     ComposerResourceReleaser* releaser);
-
-  hwc3::Error SetDisplayClientTargetCacheSize(
-      uint64_t display_id, uint32_t client_target_cache_size);
-  hwc3::Error GetDisplayClientTargetCacheSize(uint64_t display_id,
-                                              size_t* out_cache_size);
-  hwc3::Error GetDisplayOutputBufferCacheSize(uint64_t display,
-                                              size_t* out_cache_size);
-  hwc3::Error GetDisplayOutputBuffer(uint64_t display_id, const Buffer& buffer,
-                                     buffer_handle_t* out_handle,
-                                     ComposerResourceReleaser* releaser);
-
-  static std::unique_ptr<ComposerResourceReleaser> CreateResourceReleaser(
-      bool is_buffer);
-
- private:
-  ComposerResources() = default;
-
-  std::unique_ptr<
-      ::android::hardware::graphics::composer::V2_2::hal::ComposerResources>
-      resources_ = ::android::hardware::graphics::composer::V2_2::hal::
-          ComposerResources::create();
-};
-
-}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/hwc3/DrmHwcThree.cpp b/hwc3/DrmHwcThree.cpp
index 9b4ba86..6df3022 100644
--- a/hwc3/DrmHwcThree.cpp
+++ b/hwc3/DrmHwcThree.cpp
@@ -28,6 +28,16 @@
 
 namespace aidl::android::hardware::graphics::composer3::impl {
 
+auto DrmHwcThree::GetHwc3Display(::android::HwcDisplay& display)
+    -> std::shared_ptr<Hwc3Display> {
+  auto frontend_private_data = display.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc3Display>();
+    display.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc3Display>(frontend_private_data);
+}
+
 DrmHwcThree::~DrmHwcThree() {
   /* Display deinit routine is handled by resource manager */
   GetResMan().DeInit();
@@ -51,7 +61,16 @@
 }
 
 void DrmHwcThree::SendRefreshEventToClient(uint64_t display_id) {
-  composer_resources_->SetDisplayMustValidateState(display_id, true);
+  {
+    const std::unique_lock lock(GetResMan().GetMainLock());
+    auto* idisplay = GetDisplay(display_id);
+    if (idisplay == nullptr) {
+      ALOGE("Failed to get display %" PRIu64, display_id);
+      return;
+    }
+    auto hwc3_display = GetHwc3Display(*idisplay);
+    hwc3_display->must_validate = true;
+  }
   composer_callback_->onRefresh(static_cast<int64_t>(display_id));
 }
 
@@ -69,11 +88,9 @@
   switch (display_status) {
     case DrmHwc::kDisconnected:
       event = common::DisplayHotplugEvent::DISCONNECTED;
-      HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), false);
       break;
     case DrmHwc::kConnected:
       event = common::DisplayHotplugEvent::CONNECTED;
-      HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), true);
       break;
     case DrmHwc::kLinkTrainingFailed:
       event = common::DisplayHotplugEvent::ERROR_INCOMPATIBLE_CABLE;
@@ -87,28 +104,9 @@
 void DrmHwcThree::SendHotplugEventToClient(
     hwc2_display_t display_id, DrmHwc::DisplayStatus display_status) {
   bool connected = display_status != DrmHwc::kDisconnected;
-  HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), connected);
   composer_callback_->onHotplug(static_cast<int64_t>(display_id), connected);
 }
 
 #endif
 
-void DrmHwcThree::HandleDisplayHotplugEvent(uint64_t display_id,
-                                            bool connected) {
-  DEBUG_FUNC();
-  if (!connected) {
-    composer_resources_->RemoveDisplay(display_id);
-    return;
-  }
-
-  /* The second or any subsequent hotplug event with connected status enabled is
-   * a special way to inform the client (SF) that the display has changed its
-   * dimensions. In this case, the client removes all layers and re-creates
-   * them. In this case, we keep the display resources.
-   */
-  if (!composer_resources_->HasDisplay(display_id)) {
-    composer_resources_->AddPhysicalDisplay(display_id);
-  }
-}
-
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/hwc3/DrmHwcThree.h b/hwc3/DrmHwcThree.h
index 3a9a6db..44168aa 100644
--- a/hwc3/DrmHwcThree.h
+++ b/hwc3/DrmHwcThree.h
@@ -19,15 +19,18 @@
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 
 #include "drm/DrmHwc.h"
-#include "hwc3/ComposerResources.h"
+#include "hwc2_device/HwcDisplay.h"
 
 namespace aidl::android::hardware::graphics::composer3::impl {
 
+class Hwc3Display : public ::android::FrontendDisplayBase {
+ public:
+  bool must_validate = false;
+};
+
 class DrmHwcThree : public ::android::DrmHwc {
  public:
-  explicit DrmHwcThree(ComposerResources* composer_resources)
-      : composer_resources_(composer_resources) {
-  }
+  explicit DrmHwcThree() = default;
   ~DrmHwcThree() override;
 
   void Init(std::shared_ptr<IComposerCallback> callback);
@@ -41,10 +44,10 @@
   void SendHotplugEventToClient(hwc2_display_t display_id,
                                 DrmHwc::DisplayStatus display_status) override;
 
- private:
-  void HandleDisplayHotplugEvent(uint64_t display_id, bool connected);
+  static auto GetHwc3Display(::android::HwcDisplay& display)
+      -> std::shared_ptr<Hwc3Display>;
 
+ private:
   std::shared_ptr<IComposerCallback> composer_callback_;
-  ComposerResources* composer_resources_;
 };
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/hwc3/meson.build b/hwc3/meson.build
index 291c71a..c525308 100644
--- a/hwc3/meson.build
+++ b/hwc3/meson.build
@@ -4,7 +4,6 @@
     'Composer.cpp',
     'DrmHwcThree.cpp',
     'service.cpp',
-    'ComposerResources.cpp',
     'Utils.cpp',
 )
 
diff --git a/utils/properties.cpp b/utils/properties.cpp
index 5ba109b..a975822 100644
--- a/utils/properties.cpp
+++ b/utils/properties.cpp
@@ -27,7 +27,7 @@
 }
 
 auto Properties::UseConfigGroups() -> bool {
-  return (property_get_bool("ro.vendor.hwc.drm.use_config_groups", 1) != 0);
+  return (property_get_bool("ro.vendor.hwc.drm.use_config_groups", 0) != 0);
 }
 
 auto Properties::UseOverlayPlanes() -> bool {