drm_hwcomposer: Add backend-dependent validation for HwcDisplay class

Different DRM/KMS backends have a variable set of limitations, which is
not always exposed via DRM ioctls.

This implementation of backend-dependent validation provides a register
of platform-specific inherited backend class to the map by BackendManager
class. ValidateDisplay function is moved to generic backend
implementantion and separated into 2 additional methods.

The map key is a string that contains the corresponding DRM driver name.
During DrmHwcTwo class initialization the vendor.hwc.backend_override
system property and driver name will be checked and a backend will be set
for the appropriate display. If the map does not have any backend for the
named driver, the generic backend will be used.

Signed-off-by: Matvii Zorin <matvii.zorin@globallogic.com>
diff --git a/Android.bp b/Android.bp
index 8bcd1aa..b4bf8e0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -94,6 +94,9 @@
 
         "utils/autolock.cpp",
         "utils/hwcutils.cpp",
+
+        "backend/backendmanager.cpp",
+        "backend/backend.cpp",
     ],
 }
 
diff --git a/backend/backend.cpp b/backend/backend.cpp
new file mode 100644
index 0000000..08ee5a7
--- /dev/null
+++ b/backend/backend.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "backend.h"
+#include "backendmanager.h"
+#include "drmhwctwo.h"
+
+namespace android {
+
+HWC2::Error Backend::ValidateDisplay(DrmHwcTwo::HwcDisplay *display,
+                                     uint32_t *num_types,
+                                     uint32_t *num_requests) {
+  *num_types = 0;
+  *num_requests = 0;
+  size_t avail_planes = display->primary_planes().size() +
+                        display->overlay_planes().size();
+
+  /*
+   * If more layers then planes, save one plane
+   * for client composited layers
+   */
+  if (avail_planes < display->layers().size())
+    avail_planes--;
+
+  std::map<uint32_t, DrmHwcTwo::HwcLayer *> z_map, z_map_tmp;
+  uint32_t z_index = 0;
+  // First create a map of layers and z_order values
+  for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l :
+       display->layers())
+    z_map_tmp.emplace(std::make_pair(l.second.z_order(), &l.second));
+  // normalise the map so that the lowest z_order layer has key 0
+  for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map_tmp)
+    z_map.emplace(std::make_pair(z_index++, l.second));
+
+  uint32_t total_pixops = display->CalcPixOps(z_map, 0, z_map.size());
+  uint32_t gpu_pixops = 0;
+
+  int client_start = -1, client_size = 0;
+
+  if (display->compositor().ShouldFlattenOnClient()) {
+    client_start = 0;
+    client_size = z_map.size();
+    display->MarkValidated(z_map, client_start, client_size);
+  } else {
+    std::tie(client_start, client_size) = GetClientLayers(display, z_map);
+
+    int extra_client = (z_map.size() - client_size) - avail_planes;
+    if (extra_client > 0) {
+      int start = 0, steps;
+      if (client_size != 0) {
+        int prepend = std::min(client_start, extra_client);
+        int append = std::min(int(z_map.size() - (client_start + client_size)),
+                              extra_client);
+        start = client_start - prepend;
+        client_size += extra_client;
+        steps = 1 + std::min(std::min(append, prepend),
+                             int(z_map.size()) - (start + client_size));
+      } else {
+        client_size = extra_client;
+        steps = 1 + z_map.size() - extra_client;
+      }
+
+      gpu_pixops = INT_MAX;
+      for (int i = 0; i < steps; i++) {
+        uint32_t po = display->CalcPixOps(z_map, start + i, client_size);
+        if (po < gpu_pixops) {
+          gpu_pixops = po;
+          client_start = start + i;
+        }
+      }
+    }
+
+    display->MarkValidated(z_map, client_start, client_size);
+
+    bool testing_needed = !(client_start == 0 && client_size == z_map.size());
+
+    if (testing_needed &&
+        display->CreateComposition(true) != HWC2::Error::None) {
+      ++display->total_stats().failed_kms_validate_;
+      gpu_pixops = total_pixops;
+      client_size = z_map.size();
+      display->MarkValidated(z_map, 0, client_size);
+    }
+  }
+
+  *num_types = client_size;
+
+  display->total_stats().frames_flattened_ = display->compositor()
+                                                 .GetFlattenedFramesCount();
+  display->total_stats().gpu_pixops_ += gpu_pixops;
+  display->total_stats().total_pixops_ += total_pixops;
+
+  return *num_types ? HWC2::Error::HasChanges : HWC2::Error::None;
+}
+
+std::tuple<int, int> Backend::GetClientLayers(
+    DrmHwcTwo::HwcDisplay *display,
+    const std::map<uint32_t, DrmHwcTwo::HwcLayer *> &z_map) {
+  int client_start = -1, client_size = 0;
+
+  for (auto & [ z_order, layer ] : z_map) {
+    if (IsClientLayer(display, layer)) {
+      if (client_start < 0)
+        client_start = z_order;
+      client_size = (z_order - client_start) + 1;
+    }
+  }
+
+  return std::make_tuple(client_start, client_size);
+}
+
+bool Backend::IsClientLayer(DrmHwcTwo::HwcDisplay *display,
+                            DrmHwcTwo::HwcLayer *layer) {
+  return !display->HardwareSupportsLayerType(layer->sf_type()) ||
+         !display->importer()->CanImportBuffer(layer->buffer()) ||
+         display->color_transform_hint() != HAL_COLOR_TRANSFORM_IDENTITY ||
+         (layer->RequireScalingOrPhasing() &&
+          display->resource_manager()->ForcedScalingWithGpu());
+}
+
+REGISTER_BACKEND("generic", Backend);
+
+}  // namespace android
diff --git a/backend/backendmanager.cpp b/backend/backendmanager.cpp
new file mode 100644
index 0000000..80ec827
--- /dev/null
+++ b/backend/backendmanager.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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 "hwc-backend"
+
+#include "backendmanager.h"
+#include "backend.h"
+#include "drmhwctwo.h"
+
+#include <cutils/properties.h>
+#include <log/log.h>
+
+namespace android {
+
+const std::vector<std::string> BackendManager::client_devices_ = {
+    "kirin",
+};
+
+BackendManager &BackendManager::GetInstance() {
+  static BackendManager backend_manager;
+
+  return backend_manager;
+}
+
+int BackendManager::RegisterBackend(const std::string &name,
+                                    backend_constructor_t backend_constructor) {
+  available_backends_[name] = std::move(backend_constructor);
+  return 0;
+}
+
+int BackendManager::SetBackendForDisplay(DrmHwcTwo::HwcDisplay *display) {
+  std::string driver_name(display->drm()->GetName());
+  char backend_override[PROPERTY_VALUE_MAX];
+  property_get("vendor.hwc.backend_override", backend_override,
+               driver_name.c_str());
+  std::string backend_name(std::move(backend_override));
+
+  display->set_backend(GetBackendByName(backend_name));
+  if (!display->backend()) {
+    ALOGE("Failed to set backend '%s' for '%s' and driver '%s'",
+          backend_name.c_str(), display->connector()->name().c_str(),
+          driver_name.c_str());
+    return -EINVAL;
+  }
+
+  ALOGI("Backend '%s' for '%s' and driver '%s' was successfully set",
+        backend_name.c_str(), display->connector()->name().c_str(),
+        driver_name.c_str());
+
+  return 0;
+}
+
+std::unique_ptr<Backend> BackendManager::GetBackendByName(std::string &name) {
+  if (!available_backends_.size()) {
+    ALOGE("No backends are specified");
+    return nullptr;
+  }
+
+  auto it = available_backends_.find(name);
+  if (it == available_backends_.end()) {
+    auto it = std::find(client_devices_.begin(), client_devices_.end(), name);
+    name = it == client_devices_.end() ? "generic" : "client";
+  }
+
+  return available_backends_[name]();
+}
+}  // namespace android
diff --git a/drm/drmdevice.cpp b/drm/drmdevice.cpp
index d7fd2f2..91fe158 100644
--- a/drm/drmdevice.cpp
+++ b/drm/drmdevice.cpp
@@ -570,4 +570,16 @@
   return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name,
                      property);
 }
+
+const std::string DrmDevice::GetName() const {
+  auto ver = drmGetVersion(fd_.get());
+  if (!ver) {
+    ALOGW("Failed to get drm version for fd=%d", fd_.get());
+    return "generic";
+  }
+
+  std::string name(ver->name);
+  drmFreeVersion(ver);
+  return name;
+}
 }  // namespace android
diff --git a/drmhwctwo.cpp b/drmhwctwo.cpp
index a847c35..ffc45ef 100644
--- a/drmhwctwo.cpp
+++ b/drmhwctwo.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "hwc-drm-two"
 
 #include "drmhwctwo.h"
+#include "backendmanager.h"
 #include "drmdisplaycomposition.h"
 #include "drmhwcomposer.h"
 #include "platform.h"
@@ -299,6 +300,12 @@
     return HWC2::Error::BadDisplay;
   }
 
+  ret = BackendManager::GetInstance().SetBackendForDisplay(this);
+  if (ret) {
+    ALOGE("Failed to set backend for d=%d %d\n", display, ret);
+    return HWC2::Error::BadDisplay;
+  }
+
   return ChosePreferredConfig();
 }
 
@@ -900,92 +907,8 @@
 HWC2::Error DrmHwcTwo::HwcDisplay::ValidateDisplay(uint32_t *num_types,
                                                    uint32_t *num_requests) {
   supported(__func__);
-  *num_types = 0;
-  *num_requests = 0;
-  size_t avail_planes = primary_planes_.size() + overlay_planes_.size();
 
-  /*
-   * If more layers then planes, save one plane
-   * for client composited layers
-   */
-  if (avail_planes < layers_.size())
-    avail_planes--;
-
-  std::map<uint32_t, DrmHwcTwo::HwcLayer *> z_map, z_map_tmp;
-  uint32_t z_index = 0;
-  // First create a map of layers and z_order values
-  for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_)
-    z_map_tmp.emplace(std::make_pair(l.second.z_order(), &l.second));
-  // normalise the map so that the lowest z_order layer has key 0
-  for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map_tmp)
-    z_map.emplace(std::make_pair(z_index++, l.second));
-
-  uint32_t total_pixops = CalcPixOps(z_map, 0, z_map.size()), gpu_pixops = 0;
-
-  int client_start = -1, client_size = 0;
-
-  if (compositor_.ShouldFlattenOnClient()) {
-    client_start = 0;
-    client_size = z_map.size();
-    MarkValidated(z_map, client_start, client_size);
-  } else {
-    for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map) {
-      if (!HardwareSupportsLayerType(l.second->sf_type()) ||
-          !importer_->CanImportBuffer(l.second->buffer()) ||
-          color_transform_hint_ != HAL_COLOR_TRANSFORM_IDENTITY ||
-          (l.second->RequireScalingOrPhasing() &&
-           resource_manager_->ForcedScalingWithGpu())) {
-        if (client_start < 0)
-          client_start = l.first;
-        client_size = (l.first - client_start) + 1;
-      }
-    }
-
-    int extra_client = (z_map.size() - client_size) - avail_planes;
-    if (extra_client > 0) {
-      int start = 0, steps;
-      if (client_size != 0) {
-        int prepend = std::min(client_start, extra_client);
-        int append = std::min(int(z_map.size() - (client_start + client_size)),
-                              extra_client);
-        start = client_start - prepend;
-        client_size += extra_client;
-        steps = 1 + std::min(std::min(append, prepend),
-                             int(z_map.size()) - (start + client_size));
-      } else {
-        client_size = extra_client;
-        steps = 1 + z_map.size() - extra_client;
-      }
-
-      gpu_pixops = INT_MAX;
-      for (int i = 0; i < steps; i++) {
-        uint32_t po = CalcPixOps(z_map, start + i, client_size);
-        if (po < gpu_pixops) {
-          gpu_pixops = po;
-          client_start = start + i;
-        }
-      }
-    }
-
-    MarkValidated(z_map, client_start, client_size);
-
-    bool testing_needed = !(client_start == 0 && client_size == z_map.size());
-
-    if (testing_needed && CreateComposition(true) != HWC2::Error::None) {
-      ++total_stats_.failed_kms_validate_;
-      gpu_pixops = total_pixops;
-      client_size = z_map.size();
-      MarkValidated(z_map, 0, client_size);
-    }
-  }
-
-  *num_types = client_size;
-
-  total_stats_.frames_flattened_ = compositor_.GetFlattenedFramesCount();
-  total_stats_.gpu_pixops_ += gpu_pixops;
-  total_stats_.total_pixops_ += total_pixops;
-
-  return *num_types ? HWC2::Error::HasChanges : HWC2::Error::None;
+  return backend_->ValidateDisplay(this, num_types, num_requests);
 }
 
 #if PLATFORM_SDK_VERSION > 28
diff --git a/include/backend.h b/include/backend.h
new file mode 100644
index 0000000..cd9d8cd
--- /dev/null
+++ b/include/backend.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BACKEND_H
+#define ANDROID_BACKEND_H
+
+#include "drmhwctwo.h"
+
+namespace android {
+
+class Backend {
+ public:
+  virtual ~Backend() = default;
+  virtual HWC2::Error ValidateDisplay(DrmHwcTwo::HwcDisplay *display,
+                                      uint32_t *num_types,
+                                      uint32_t *num_requests);
+  virtual std::tuple<int, int> GetClientLayers(
+      DrmHwcTwo::HwcDisplay *display,
+      const std::map<uint32_t, DrmHwcTwo::HwcLayer *> &z_map);
+  virtual bool IsClientLayer(DrmHwcTwo::HwcDisplay *display,
+                             DrmHwcTwo::HwcLayer *layer);
+};
+}  // namespace android
+
+#endif
diff --git a/include/backendmanager.h b/include/backendmanager.h
new file mode 100644
index 0000000..d141652
--- /dev/null
+++ b/include/backendmanager.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BACKEND_MANAGER_H
+#define ANDROID_BACKEND_MANAGER_H
+
+#include "backend.h"
+#include "drmhwctwo.h"
+
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#define REGISTER_BACKEND(name_str_, backend_)                               \
+  static int                                                                \
+      backend = BackendManager::GetInstance()                               \
+                    .RegisterBackend(name_str_,                             \
+                                     []() -> std::unique_ptr<Backend> {     \
+                                       return std::make_unique<backend_>(); \
+                                     });
+
+namespace android {
+
+class BackendManager {
+ public:
+  using backend_constructor_t = std::function<std::unique_ptr<Backend>()>;
+  static BackendManager &GetInstance();
+  int RegisterBackend(const std::string &name,
+                      backend_constructor_t backend_constructor);
+  int SetBackendForDisplay(DrmHwcTwo::HwcDisplay *display);
+  std::unique_ptr<Backend> GetBackendByName(std::string &name);
+  HWC2::Error ValidateDisplay(DrmHwcTwo::HwcDisplay *display,
+                              uint32_t *num_types, uint32_t *num_requests);
+
+ private:
+  BackendManager() = default;
+
+  static const std::vector<std::string> client_devices_;
+
+  std::map<std::string, backend_constructor_t> available_backends_;
+};
+}  // namespace android
+
+#endif
diff --git a/include/drmdevice.h b/include/drmdevice.h
index 91dd38b..1f08f20 100644
--- a/include/drmdevice.h
+++ b/include/drmdevice.h
@@ -70,6 +70,8 @@
   int GetConnectorProperty(const DrmConnector &connector, const char *prop_name,
                            DrmProperty *property);
 
+  const std::string GetName() const;
+
   const std::vector<std::unique_ptr<DrmCrtc>> &crtcs() const;
   uint32_t next_mode_id();
 
diff --git a/include/drmhwctwo.h b/include/drmhwctwo.h
index df75c41..8b1be4b 100644
--- a/include/drmhwctwo.h
+++ b/include/drmhwctwo.h
@@ -31,6 +31,8 @@
 
 namespace android {
 
+class Backend;
+
 class DrmHwcTwo : public hwc2_device_t {
  public:
   static int HookDevOpen(const struct hw_module_t *module, const char *name,
@@ -256,6 +258,13 @@
       uint32_t frames_flattened_ = 0;
     };
 
+    const Backend *backend() const {
+      return backend_.get();
+    }
+    void set_backend(std::unique_ptr<Backend> backend) {
+      backend_ = std::move(backend);
+    }
+
     const std::vector<DrmPlane *> &primary_planes() const {
       return primary_planes_;
     }
@@ -310,6 +319,8 @@
     std::vector<DrmPlane *> primary_planes_;
     std::vector<DrmPlane *> overlay_planes_;
 
+    std::unique_ptr<Backend> backend_;
+
     VSyncWorker vsync_worker_;
     DrmConnector *connector_ = NULL;
     DrmCrtc *crtc_ = NULL;