drm_hwcomposer: Presentation to cursor plane

This change adds support for presenting to the cursor plane.
Logic is added to match cursor layers with cursor planes during
composition, plus additional test commit logic with backoff
behavior. Prior to this change, cursor planes were never
considered during composition. After this change, cursor
planes are available to be matched with compatible layers and
then used for presentation.

Change-Id: I3acd131cd210de46ff19af5a8960b07a82f462b2
Signed-off-by: Andrew Wolfers <aswolfers@google.com>
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index 650e29e..12a5fea 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -20,9 +20,20 @@
 
 #include "BackendManager.h"
 #include "bufferinfo/BufferInfoGetter.h"
+#include "hardware/hwcomposer2.h"
 
 namespace android {
 
+namespace {
+
+bool HasCursorLayer(const std::vector<HwcLayer *> &layers) {
+  return std::find_if(layers.begin(), layers.end(), [&](auto *layer) -> bool {
+           return layer->GetSfType() == HWC2::Composition::Cursor;
+         }) != layers.end();
+}
+
+}  // namespace
+
 HWC2::Error Backend::ValidateDisplay(HwcDisplay *display, uint32_t *num_types,
                                      uint32_t *num_requests) {
   *num_types = 0;
@@ -62,7 +73,27 @@
     ++display->total_stats().failed_kms_validate_;
     client_start = 0;
     client_size = layers.size();
-    MarkValidated(layers, 0, client_size);
+
+    // Expand the client range to include all layers except the cursor layer (if
+    // there is one) and retry.
+    auto [_, cursor_plane] = display->GetPipe().GetUsablePlanes();
+    if (cursor_plane && HasCursorLayer(layers)) {
+      --client_size;
+      MarkValidated(layers, 0, client_size);
+
+      testing_needed = display->CreateComposition(a_args) != HWC2::Error::None;
+
+      // If testing is still needed, expand the client range to include the
+      // cursor layer for the next retry.
+      if (testing_needed) {
+        ++client_size;
+        ++display->total_stats().failed_kms_validate_;
+      }
+    }
+
+    if (testing_needed) {
+      MarkValidated(layers, 0, client_size);
+    }
   }
 
   *num_types = client_size;
@@ -120,10 +151,13 @@
 void Backend::MarkValidated(std::vector<HwcLayer *> &layers,
                             size_t client_first_z, size_t client_size) {
   for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
-    if (z_order >= client_first_z && z_order < client_first_z + client_size)
+    if (z_order >= client_first_z && z_order < client_first_z + client_size) {
       layers[z_order]->SetValidatedType(HWC2::Composition::Client);
-    else
+    } else if (layers[z_order]->GetSfType() == HWC2::Composition::Cursor) {
+      layers[z_order]->SetValidatedType(HWC2::Composition::Cursor);
+    } else {
       layers[z_order]->SetValidatedType(HWC2::Composition::Device);
+    }
   }
 }
 
@@ -132,31 +166,39 @@
     int client_start, size_t client_size) {
   auto [planes, cursor_plane] = display->GetPipe().GetUsablePlanes();
   size_t avail_planes = planes.size();
+  size_t layers_size = layers.size();
+
+  // |cursor_plane| is not counted among |avail_planes|, so the cursor layer
+  // shouldn't be counted in |layers_size|.
+  if (cursor_plane && HasCursorLayer(layers)) {
+    --layers_size;
+  }
 
   /*
-   * If more layers then planes, save one plane
+   * If more layers than planes, save one plane
    * for client composited layers
    */
-  if (avail_planes < display->layers().size())
+  if (avail_planes < layers_size) {
     avail_planes--;
+  }
 
-  const int extra_client = int(layers.size() - client_size) - int(avail_planes);
+  const int extra_client = int(layers_size - client_size) - int(avail_planes);
 
   if (extra_client > 0) {
     int start = 0;
     size_t steps = 0;
     if (client_size != 0) {
       const int prepend = std::min(client_start, extra_client);
-      const int append = std::min(int(layers.size()) -
+      const int append = std::min(int(layers_size) -
                                       int(client_start + client_size),
                                   extra_client);
       start = client_start - (int)prepend;
       client_size += extra_client;
       steps = 1 + std::min(std::min(append, prepend),
-                           int(layers.size()) - int(start + client_size));
+                           int(layers_size) - int(start + client_size));
     } else {
       client_size = extra_client;
-      steps = 1 + layers.size() - extra_client;
+      steps = 1 + layers_size - extra_client;
     }
 
     uint32_t gpu_pixops = UINT32_MAX;
diff --git a/compositor/DrmKmsPlan.cpp b/compositor/DrmKmsPlan.cpp
index 443b515..1155697 100644
--- a/compositor/DrmKmsPlan.cpp
+++ b/compositor/DrmKmsPlan.cpp
@@ -23,17 +23,30 @@
 #include "utils/log.h"
 
 namespace android {
-auto DrmKmsPlan::CreateDrmKmsPlan(DrmDisplayPipeline &pipe,
-                                  std::vector<LayerData> composition)
-    -> std::unique_ptr<DrmKmsPlan> {
+auto DrmKmsPlan::CreateDrmKmsPlan(
+    DrmDisplayPipeline &pipe, std::vector<LayerData> composition,
+    std::optional<LayerData> cursor_layer) -> std::unique_ptr<DrmKmsPlan> {
   auto plan = std::make_unique<DrmKmsPlan>();
 
   auto [avail_planes, cursor_plane] = pipe.GetUsablePlanes();
 
   int z_pos = 0;
+  if (cursor_layer.has_value()) {
+    if (cursor_plane &&
+        cursor_plane->Get()->IsValidForLayer(&cursor_layer.value())) {
+      plan->plan.emplace_back(
+          LayerToPlaneJoining{.layer = std::move(cursor_layer.value()),
+                              .plane = cursor_plane,
+                              .z_pos = z_pos++});
+    } else {
+      // Cursor layer can't use cursor plane, so let it match normally with
+      // others.
+      composition.push_back(std::move(cursor_layer.value()));
+    }
+  }
+
   for (auto &dhl : composition) {
     std::shared_ptr<BindingOwner<DrmPlane>> plane;
-
     /* Skip unsupported planes */
     do {
       if (avail_planes.empty()) {
diff --git a/compositor/DrmKmsPlan.h b/compositor/DrmKmsPlan.h
index 054cd93..d0b271a 100644
--- a/compositor/DrmKmsPlan.h
+++ b/compositor/DrmKmsPlan.h
@@ -35,8 +35,9 @@
   std::vector<LayerToPlaneJoining> plan;
 
   static auto CreateDrmKmsPlan(DrmDisplayPipeline &pipe,
-                               std::vector<LayerData> composition)
-      -> std::unique_ptr<DrmKmsPlan>;
+                               std::vector<LayerData> composition,
+                               std::optional<LayerData> cursor_layer =
+                                   std::nullopt) -> std::unique_ptr<DrmKmsPlan>;
 };
 
 }  // namespace android
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index a438263..82f826b 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -740,11 +740,21 @@
   bool use_client_layer = false;
   uint32_t client_z_order = UINT32_MAX;
   std::map<uint32_t, HwcLayer *> z_map;
+  std::optional<LayerData> cursor_layer = std::nullopt;
   for (auto &[_, layer] : layers_) {
     switch (layer.GetValidatedType()) {
       case HWC2::Composition::Device:
         z_map.emplace(layer.GetZOrder(), &layer);
         break;
+      case HWC2::Composition::Cursor:
+        if (!cursor_layer.has_value()) {
+          layer.PopulateLayerData();
+          cursor_layer = layer.GetLayerData();
+        } else {
+          ALOGW("Detected multiple cursor layers");
+          z_map.emplace(layer.GetZOrder(), &layer);
+        }
+        break;
       case HWC2::Composition::Client:
         // Place it at the z_order of the lowest client layer
         use_client_layer = true;
@@ -794,7 +804,8 @@
    * in between of ValidateDisplay() and PresentDisplay() calls
    */
   current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(),
-                                               std::move(composition_layers));
+                                               std::move(composition_layers),
+                                               cursor_layer);
 
   if (type_ == HWC2::DisplayType::Virtual) {
     writeback_layer_->PopulateLayerData();
@@ -1035,6 +1046,12 @@
 
   std::sort(std::begin(ordered_layers), std::end(ordered_layers),
             [](const HwcLayer *lhs, const HwcLayer *rhs) {
+              // Cursor layers should always have highest zpos.
+              if ((lhs->GetSfType() == HWC2::Composition::Cursor) !=
+                  (rhs->GetSfType() == HWC2::Composition::Cursor)) {
+                return rhs->GetSfType() == HWC2::Composition::Cursor;
+              }
+
               return lhs->GetZOrder() < rhs->GetZOrder();
             });