android: Introduce the Android buffer info abstraction
authorRoman Stratiienko <r.stratiienko@gmail.com>
Tue, 23 Aug 2022 17:08:22 +0000 (20:08 +0300)
committerMarge Bot <emma+marge@anholt.net>
Sat, 12 Aug 2023 18:46:57 +0000 (18:46 +0000)
Both EGL and Vulkan implementations require obtaining buffer metadata,
e.g., format, modifier, offsets, strides, etc.

Currently, mesa3d doesn't have a generic solution, and every Vulkan
implementation uses its getters. Most of the getters rely on
kernel metadata storage that is available for x86-based GPU drivers.

ARM-based Vulkan drivers rely on userspace metadata sharing, making it
important to use advanced metadata API. Otherwise, the driver will work
with limited functionality (no YUV, lack of support for modifiers, etc.)

Current EGL buffer getter implementation is advanced enough and used as
a base for a common Android buffer-getter logic.

Use example:

    void
    android_buffer_test(android_handle_type *a_handle)
    {
       // First, get the gralloc object. It will be created if it doesn't
       // exist. Use U_GRALLOC_TYPE_AUTO to let the implementation choose
       // the best gralloc
       struct u_gralloc *gralloc = u_gralloc_create(U_GRALLOC_TYPE_AUTO);

       // Prepare the internal handle structure (hal_format and
       // pixel_stride are required for the fallback implementation).
       // Both Vulkan and EGL clients expose HAL format / pixel stride
       // in their structures.
       u_gralloc_buffer_handle hnd = {
          .handle = a_handle->native_handle,
          .hal_format = a_handle->hal_format,
          .pixel_stride = a_handle->pixel_stride,
       };

       // Get the basic buffer info
       u_gralloc_buffer_basic_info basic_info;
       int ret = u_gralloc_get_buffer_basic_info(gralloc, &hnd, &basic_info);
       if (ret) {
          // Handle the error
       }

       // Get the color info
       u_gralloc_buffer_color_info color_info;
       ret = u_gralloc_get_buffer_color_info(gralloc, &hnd, &color_info);
       if (ret) {
          // Handle the error
       }

       // unref the gralloc object
       u_gralloc_destroy(&gralloc);
    }

Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
Reviewed-by: Chia-I Wu <olvaffe@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18215>

src/util/meson.build
src/util/u_gralloc/meson.build [new file with mode: 0644]
src/util/u_gralloc/u_gralloc.c [new file with mode: 0644]
src/util/u_gralloc/u_gralloc.h [new file with mode: 0644]
src/util/u_gralloc/u_gralloc_cros_api.c [new file with mode: 0644]
src/util/u_gralloc/u_gralloc_fallback.c [new file with mode: 0644]
src/util/u_gralloc/u_gralloc_imapper4_api.cpp [new file with mode: 0644]
src/util/u_gralloc/u_gralloc_internal.h [new file with mode: 0644]

index c0c1b9d..4ac84b1 100644 (file)
 # util is self contained.
 inc_util = [inc_include, include_directories('..')]
 
+if with_platform_android
+subdir('u_gralloc')
+endif
+
 subdir('blake3')
 
 files_mesa_util = files(
diff --git a/src/util/u_gralloc/meson.build b/src/util/u_gralloc/meson.build
new file mode 100644 (file)
index 0000000..14e2cd0
--- /dev/null
@@ -0,0 +1,35 @@
+# Mesa 3-D graphics library
+#
+# Copyright (C) 2022 Roman Stratiienko (r.stratiienko@gmail.com)
+# SPDX-License-Identifier: MIT
+
+c_args_for_u_gralloc = []
+cpp_args_for_u_gralloc = []
+options_for_u_gralloc = []
+
+files_u_gralloc = files(
+  'u_gralloc.c',
+  'u_gralloc_fallback.c',
+  'u_gralloc_cros_api.c',
+)
+
+if dep_android_mapper4.found()
+  files_u_gralloc += files('u_gralloc_imapper4_api.cpp')
+  c_args_for_u_gralloc += '-DUSE_IMAPPER4_METADATA_API'
+  cpp_args_for_u_gralloc += '-DUSE_IMAPPER4_METADATA_API'
+  options_for_u_gralloc += 'cpp_std=c++17'
+endif
+
+_libmesa_u_gralloc = static_library(
+  '_mesa_u_gralloc',
+  [files_u_gralloc],
+  include_directories : [inc_include, inc_util],
+  c_args : c_args_for_u_gralloc,
+  cpp_args : cpp_args_for_u_gralloc,
+  override_options : options_for_u_gralloc,
+  gnu_symbol_visibility : 'hidden',
+  build_by_default : false,
+  dependencies: dep_android,
+)
+
+idep_u_gralloc = declare_dependency(link_with: _libmesa_u_gralloc)
diff --git a/src/util/u_gralloc/u_gralloc.c b/src/util/u_gralloc/u_gralloc.c
new file mode 100644 (file)
index 0000000..0d1e5f6
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2022 Roman Stratiienko (r.stratiienko@gmail.com)
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "u_gralloc_internal.h"
+
+#include <assert.h>
+#include <errno.h>
+
+#include "drm-uapi/drm_fourcc.h"
+#include "util/log.h"
+#include "util/macros.h"
+#include "util/simple_mtx.h"
+#include "util/u_atomic.h"
+#include "util/u_memory.h"
+
+static simple_mtx_t u_gralloc_mutex = SIMPLE_MTX_INITIALIZER;
+
+static const struct u_grallocs {
+   enum u_gralloc_type type;
+   struct u_gralloc *(*create)();
+} u_grallocs[] = {
+   {.type = U_GRALLOC_TYPE_CROS, .create = u_gralloc_cros_api_create},
+#ifdef USE_IMAPPER4_METADATA_API
+   {.type = U_GRALLOC_TYPE_GRALLOC4, .create = u_gralloc_imapper_api_create},
+#endif /* USE_IMAPPER4_METADATA_API */
+   {.type = U_GRALLOC_TYPE_FALLBACK, .create = u_gralloc_fallback_create},
+};
+
+static struct u_gralloc_cache {
+   struct u_gralloc *u_gralloc;
+   int refcount;
+} u_gralloc_cache[U_GRALLOC_TYPE_COUNT] = {0};
+
+struct u_gralloc *
+u_gralloc_create(enum u_gralloc_type type)
+{
+   struct u_gralloc *out_gralloc = NULL;
+
+   simple_mtx_lock(&u_gralloc_mutex);
+
+   if (u_gralloc_cache[type].u_gralloc != NULL) {
+      u_gralloc_cache[type].refcount++;
+      out_gralloc = u_gralloc_cache[type].u_gralloc;
+      goto out;
+   }
+
+   for (int i = 0; i < ARRAY_SIZE(u_grallocs); i++) {
+      if (u_grallocs[i].type != type && type != U_GRALLOC_TYPE_AUTO)
+         continue;
+
+      u_gralloc_cache[type].u_gralloc = u_grallocs[i].create();
+      if (u_gralloc_cache[type].u_gralloc) {
+         assert(u_gralloc_cache[type].u_gralloc->ops.get_buffer_basic_info);
+         assert(u_gralloc_cache[type].u_gralloc->ops.destroy);
+
+         u_gralloc_cache[type].refcount = 1;
+
+         out_gralloc = u_gralloc_cache[type].u_gralloc;
+         goto out;
+      }
+   }
+
+out:
+   simple_mtx_unlock(&u_gralloc_mutex);
+
+   return out_gralloc;
+}
+
+void
+u_gralloc_destroy(struct u_gralloc **gralloc NONNULL)
+{
+   int i;
+
+   if (*gralloc == NULL)
+      return;
+
+   simple_mtx_lock(&u_gralloc_mutex);
+
+   for (i = 0; i < ARRAY_SIZE(u_gralloc_cache); i++) {
+      if (u_gralloc_cache[i].u_gralloc == *gralloc) {
+         u_gralloc_cache[i].refcount--;
+         if (u_gralloc_cache[i].refcount == 0) {
+            u_gralloc_cache[i].u_gralloc->ops.destroy(
+               u_gralloc_cache[i].u_gralloc);
+            u_gralloc_cache[i].u_gralloc = NULL;
+         }
+         break;
+      }
+   }
+
+   simple_mtx_unlock(&u_gralloc_mutex);
+
+   assert(i < ARRAY_SIZE(u_grallocs));
+
+   *gralloc = NULL;
+}
+
+inline int
+u_gralloc_get_buffer_basic_info(struct u_gralloc *gralloc NONNULL,
+                                struct u_gralloc_buffer_handle *hnd NONNULL,
+                                struct u_gralloc_buffer_basic_info *out
+                                   NONNULL)
+{
+   struct u_gralloc_buffer_basic_info info = {0};
+   int ret;
+
+   ret = gralloc->ops.get_buffer_basic_info(gralloc, hnd, &info);
+
+   if (ret)
+      return ret;
+
+   *out = info;
+
+   return 0;
+}
+
+inline int
+u_gralloc_get_buffer_color_info(struct u_gralloc *gralloc NONNULL,
+                                struct u_gralloc_buffer_handle *hnd NONNULL,
+                                struct u_gralloc_buffer_color_info *out
+                                   NONNULL)
+{
+   struct u_gralloc_buffer_color_info info = {0};
+   int ret;
+
+   if (!gralloc->ops.get_buffer_color_info)
+      return -ENOTSUP;
+
+   ret = gralloc->ops.get_buffer_color_info(gralloc, hnd, &info);
+
+   if (ret)
+      return ret;
+
+   *out = info;
+
+   return 0;
+}
+
+inline int
+u_gralloc_get_front_rendering_usage(struct u_gralloc *gralloc NONNULL,
+                                    uint64_t *out_usage NONNULL)
+{
+   if (!gralloc->ops.get_front_rendering_usage)
+      return -ENOTSUP;
+
+   return gralloc->ops.get_front_rendering_usage(gralloc, out_usage);
+}
diff --git a/src/util/u_gralloc/u_gralloc.h b/src/util/u_gralloc/u_gralloc.h
new file mode 100644 (file)
index 0000000..596e3a3
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2022 Roman Stratiienko (r.stratiienko@gmail.com)
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef U_GRALLOC_H
+#define U_GRALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <cutils/native_handle.h>
+
+#include <stdbool.h>
+
+#include "util/macros.h"
+#include "GL/internal/dri_interface.h"
+
+struct u_gralloc;
+
+/* Both Vulkan and EGL API exposes HAL format / pixel stride which is required
+ * by the fallback implementation.
+ */
+struct u_gralloc_buffer_handle {
+   const native_handle_t *handle;
+   int hal_format;
+   int pixel_stride;
+};
+
+struct u_gralloc_buffer_basic_info {
+   uint32_t drm_fourcc;
+   uint64_t modifier;
+
+   int num_planes;
+   int fds[4];
+   int offsets[4];
+   int strides[4];
+};
+
+struct u_gralloc_buffer_color_info {
+   enum __DRIYUVColorSpace yuv_color_space;
+   enum __DRISampleRange sample_range;
+   enum __DRIChromaSiting horizontal_siting;
+   enum __DRIChromaSiting vertical_siting;
+};
+
+enum u_gralloc_type {
+   U_GRALLOC_TYPE_AUTO,
+#ifdef USE_IMAPPER4_METADATA_API
+   U_GRALLOC_TYPE_GRALLOC4,
+#endif
+   U_GRALLOC_TYPE_CROS,
+   U_GRALLOC_TYPE_FALLBACK,
+   U_GRALLOC_TYPE_COUNT,
+};
+
+struct u_gralloc *u_gralloc_create(enum u_gralloc_type type);
+
+void u_gralloc_destroy(struct u_gralloc **gralloc NONNULL);
+
+int u_gralloc_get_buffer_basic_info(
+   struct u_gralloc *gralloc NONNULL,
+   struct u_gralloc_buffer_handle *hnd NONNULL,
+   struct u_gralloc_buffer_basic_info *out NONNULL);
+
+int u_gralloc_get_buffer_color_info(
+   struct u_gralloc *gralloc NONNULL,
+   struct u_gralloc_buffer_handle *hnd NONNULL,
+   struct u_gralloc_buffer_color_info *out NONNULL);
+
+int u_gralloc_get_front_rendering_usage(struct u_gralloc *gralloc NONNULL,
+                                        uint64_t *out_usage NONNULL);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* U_GRALLOC_H */
diff --git a/src/util/u_gralloc/u_gralloc_cros_api.c b/src/util/u_gralloc/u_gralloc_cros_api.c
new file mode 100644 (file)
index 0000000..a94e7fe
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright © 2021, Google Inc.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <hardware/gralloc.h>
+
+#include "util/log.h"
+#include "util/u_memory.h"
+
+#include "u_gralloc_internal.h"
+
+/* More recent CrOS gralloc has a perform op that fills out the struct below
+ * with canonical information about the buffer and its modifier, planes,
+ * offsets and strides.  If we have this, we can skip straight to
+ * createImageFromDmaBufs2() and avoid all the guessing and recalculations.
+ * This also gives us the modifier and plane offsets/strides for multiplanar
+ * compressed buffers (eg Intel CCS buffers) in order to make that work in
+ * Android.
+ */
+
+struct cros_gralloc {
+   struct u_gralloc base;
+   gralloc_module_t *gralloc_module;
+};
+
+static const char cros_gralloc_module_name[] = "CrOS Gralloc";
+
+#define CROS_GRALLOC_DRM_GET_BUFFER_INFO               4
+#define CROS_GRALLOC_DRM_GET_USAGE                     5
+#define CROS_GRALLOC_DRM_GET_USAGE_FRONT_RENDERING_BIT 0x1
+
+struct cros_gralloc0_buffer_info {
+   uint32_t drm_fourcc;
+   int num_fds;
+   int fds[4];
+   uint64_t modifier;
+   int offset[4];
+   int stride[4];
+};
+
+static int
+cros_get_buffer_info(struct u_gralloc *gralloc,
+                     struct u_gralloc_buffer_handle *hnd,
+                     struct u_gralloc_buffer_basic_info *out)
+{
+   struct cros_gralloc0_buffer_info info;
+   struct cros_gralloc *gr = (struct cros_gralloc *)gralloc;
+   gralloc_module_t *gr_mod = gr->gralloc_module;
+
+   if (gr_mod->perform(gr_mod, CROS_GRALLOC_DRM_GET_BUFFER_INFO, hnd->handle,
+                       &info) == 0) {
+      out->drm_fourcc = info.drm_fourcc;
+      out->modifier = info.modifier;
+      out->num_planes = info.num_fds;
+      for (int i = 0; i < out->num_planes; i++) {
+         out->fds[i] = info.fds[i];
+         out->offsets[i] = info.offset[i];
+         out->strides[i] = info.stride[i];
+      }
+
+      return 0;
+   }
+
+   return -EINVAL;
+}
+
+static int
+cros_get_front_rendering_usage(struct u_gralloc *gralloc, uint64_t *out_usage)
+{
+   struct cros_gralloc *gr = (struct cros_gralloc *)gralloc;
+   uint32_t front_rendering_usage = 0;
+
+   if (gr->gralloc_module->perform(
+          gr->gralloc_module, CROS_GRALLOC_DRM_GET_USAGE,
+          CROS_GRALLOC_DRM_GET_USAGE_FRONT_RENDERING_BIT,
+          &front_rendering_usage) == 0) {
+      *out_usage = front_rendering_usage;
+      return 0;
+   }
+
+   return -ENOTSUP;
+}
+
+static int
+destroy(struct u_gralloc *gralloc)
+{
+   struct cros_gralloc *gr = (struct cros_gralloc *)gralloc;
+   if (gr->gralloc_module)
+      dlclose(gr->gralloc_module->common.dso);
+
+   FREE(gr);
+
+   return 0;
+}
+
+struct u_gralloc *
+u_gralloc_cros_api_create()
+{
+   struct cros_gralloc *gr = CALLOC_STRUCT(cros_gralloc);
+   int err = 0;
+
+   err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                       (const hw_module_t **)&gr->gralloc_module);
+
+   if (err)
+      goto fail;
+
+   if (strcmp(gr->gralloc_module->common.name, cros_gralloc_module_name) != 0)
+      goto fail;
+
+   if (!gr->gralloc_module->perform) {
+      mesa_logw("Oops. CrOS gralloc doesn't have perform callback");
+      goto fail;
+   }
+
+   gr->base.ops.get_buffer_basic_info = cros_get_buffer_info;
+   gr->base.ops.get_front_rendering_usage = cros_get_front_rendering_usage;
+   gr->base.ops.destroy = destroy;
+
+   mesa_logi("Using gralloc0 CrOS API");
+
+   return &gr->base;
+
+fail:
+   destroy(&gr->base);
+
+   return NULL;
+}
diff --git a/src/util/u_gralloc/u_gralloc_fallback.c b/src/util/u_gralloc/u_gralloc_fallback.c
new file mode 100644 (file)
index 0000000..27999a7
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright © 2021, Google Inc.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "u_gralloc_internal.h"
+
+#include <hardware/gralloc.h>
+
+#include "drm-uapi/drm_fourcc.h"
+#include "util/log.h"
+#include "util/macros.h"
+#include "util/u_memory.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+struct fallback_gralloc {
+   struct u_gralloc base;
+   gralloc_module_t *gralloc_module;
+};
+
+enum chroma_order {
+   YCbCr,
+   YCrCb,
+};
+
+struct droid_yuv_format {
+   /* Lookup keys */
+   int native;                     /* HAL_PIXEL_FORMAT_ */
+   enum chroma_order chroma_order; /* chroma order is {Cb, Cr} or {Cr, Cb} */
+   int chroma_step; /* Distance in bytes between subsequent chroma pixels. */
+
+   /* Result */
+   int fourcc; /* DRM_FORMAT_ */
+};
+
+/* The following table is used to look up a DRI image FourCC based
+ * on native format and information contained in android_ycbcr struct. */
+static const struct droid_yuv_format droid_yuv_formats[] = {
+   /* Native format, YCrCb, Chroma step, DRI image FourCC */
+   {HAL_PIXEL_FORMAT_YCbCr_420_888, YCbCr, 2, DRM_FORMAT_NV12},
+   {HAL_PIXEL_FORMAT_YCbCr_420_888, YCbCr, 1, DRM_FORMAT_YUV420},
+   {HAL_PIXEL_FORMAT_YCbCr_420_888, YCrCb, 1, DRM_FORMAT_YVU420},
+   {HAL_PIXEL_FORMAT_YV12, YCrCb, 1, DRM_FORMAT_YVU420},
+   /* HACK: See droid_create_image_from_prime_fds() and
+    * https://issuetracker.google.com/32077885. */
+   {HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, YCbCr, 2, DRM_FORMAT_NV12},
+   {HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, YCbCr, 1, DRM_FORMAT_YUV420},
+   {HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, YCrCb, 1, DRM_FORMAT_YVU420},
+   {HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, YCrCb, 1, DRM_FORMAT_AYUV},
+   {HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, YCrCb, 1, DRM_FORMAT_XYUV8888},
+};
+
+static int
+get_fourcc_yuv(int native, enum chroma_order chroma_order, int chroma_step)
+{
+   for (int i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
+      if (droid_yuv_formats[i].native == native &&
+          droid_yuv_formats[i].chroma_order == chroma_order &&
+          droid_yuv_formats[i].chroma_step == chroma_step)
+         return droid_yuv_formats[i].fourcc;
+
+   return -1;
+}
+
+static bool
+is_yuv(int native)
+{
+   for (int i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
+      if (droid_yuv_formats[i].native == native)
+         return true;
+
+   return false;
+}
+
+static int
+get_format_bpp(int native)
+{
+   int bpp;
+
+   switch (native) {
+   case HAL_PIXEL_FORMAT_RGBA_FP16:
+      bpp = 8;
+      break;
+   case HAL_PIXEL_FORMAT_RGBA_8888:
+   case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      /*
+       * HACK: Hardcode this to RGBX_8888 as per cros_gralloc hack.
+       * TODO: Remove this once https://issuetracker.google.com/32077885 is
+       * fixed.
+       */
+   case HAL_PIXEL_FORMAT_RGBX_8888:
+   case HAL_PIXEL_FORMAT_BGRA_8888:
+   case HAL_PIXEL_FORMAT_RGBA_1010102:
+      bpp = 4;
+      break;
+   case HAL_PIXEL_FORMAT_RGB_565:
+      bpp = 2;
+      break;
+   default:
+      bpp = 0;
+      break;
+   }
+
+   return bpp;
+}
+
+/* createImageFromFds requires fourcc format */
+static int
+get_fourcc(int native)
+{
+   switch (native) {
+   case HAL_PIXEL_FORMAT_RGB_565:
+      return DRM_FORMAT_RGB565;
+   case HAL_PIXEL_FORMAT_BGRA_8888:
+      return DRM_FORMAT_ARGB8888;
+   case HAL_PIXEL_FORMAT_RGBA_8888:
+      return DRM_FORMAT_ABGR8888;
+   case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+      /*
+       * HACK: Hardcode this to RGBX_8888 as per cros_gralloc hack.
+       * TODO: Remove this once https://issuetracker.google.com/32077885 is
+       * fixed.
+       */
+   case HAL_PIXEL_FORMAT_RGBX_8888:
+      return DRM_FORMAT_XBGR8888;
+   case HAL_PIXEL_FORMAT_RGBA_FP16:
+      return DRM_FORMAT_ABGR16161616F;
+   case HAL_PIXEL_FORMAT_RGBA_1010102:
+      return DRM_FORMAT_ABGR2101010;
+   default:
+      mesa_logw("unsupported native buffer format 0x%x", native);
+   }
+   return -1;
+}
+
+/* returns # of fds, and by reference the actual fds */
+static unsigned
+get_native_buffer_fds(const native_handle_t *handle, int fds[3])
+{
+   if (!handle)
+      return 0;
+
+   /*
+    * Various gralloc implementations exist, but the dma-buf fd tends
+    * to be first. Access it directly to avoid a dependency on specific
+    * gralloc versions.
+    */
+   for (int i = 0; i < handle->numFds; i++)
+      fds[i] = handle->data[i];
+
+   return handle->numFds;
+}
+
+static int
+fallback_gralloc_get_yuv_info(struct u_gralloc *gralloc,
+                              struct u_gralloc_buffer_handle *hnd,
+                              struct u_gralloc_buffer_basic_info *out)
+{
+   struct fallback_gralloc *gr = (struct fallback_gralloc *)gralloc;
+   gralloc_module_t *gr_mod = gr->gralloc_module;
+   enum chroma_order chroma_order;
+   struct android_ycbcr ycbcr;
+   int drm_fourcc = 0;
+   int num_planes = 0;
+   int num_fds = 0;
+   int fds[3];
+   int ret;
+
+   num_fds = get_native_buffer_fds(hnd->handle, fds);
+   if (num_fds == 0)
+      return -EINVAL;
+
+   if (!gr_mod || !gr_mod->lock_ycbcr) {
+      return -EINVAL;
+   }
+
+   memset(&ycbcr, 0, sizeof(ycbcr));
+   ret = gr_mod->lock_ycbcr(gr_mod, hnd->handle, 0, 0, 0, 0, 0, &ycbcr);
+   if (ret) {
+      /* HACK: See native_window_buffer_get_buffer_info() and
+       * https://issuetracker.google.com/32077885.*/
+      if (hnd->hal_format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)
+         return -EAGAIN;
+
+      mesa_logw("gralloc->lock_ycbcr failed: %d", ret);
+      return -EINVAL;
+   }
+   gr_mod->unlock(gr_mod, hnd->handle);
+
+   chroma_order = ((size_t)ycbcr.cr < (size_t)ycbcr.cb) ? YCrCb : YCbCr;
+
+   /* .chroma_step is the byte distance between the same chroma channel
+    * values of subsequent pixels, assumed to be the same for Cb and Cr. */
+   drm_fourcc =
+      get_fourcc_yuv(hnd->hal_format, chroma_order, ycbcr.chroma_step);
+   if (drm_fourcc == -1) {
+      mesa_logw("unsupported YUV format, native = %x, chroma_order = %s, "
+                "chroma_step = %zu",
+                hnd->hal_format, chroma_order == YCbCr ? "YCbCr" : "YCrCb",
+                ycbcr.chroma_step);
+      return -EINVAL;
+   }
+
+   out->drm_fourcc = drm_fourcc;
+   out->modifier = DRM_FORMAT_MOD_INVALID;
+
+   num_planes = ycbcr.chroma_step == 2 ? 2 : 3;
+   /* When lock_ycbcr's usage argument contains no SW_READ/WRITE flags
+    * it will return the .y/.cb/.cr pointers based on a NULL pointer,
+    * so they can be interpreted as offsets. */
+   out->offsets[0] = (size_t)ycbcr.y;
+   /* We assume here that all the planes are located in one DMA-buf. */
+   if (chroma_order == YCrCb) {
+      out->offsets[1] = (size_t)ycbcr.cr;
+      out->offsets[2] = (size_t)ycbcr.cb;
+   } else {
+      out->offsets[1] = (size_t)ycbcr.cb;
+      out->offsets[2] = (size_t)ycbcr.cr;
+   }
+
+   /* .ystride is the line length (in bytes) of the Y plane,
+    * .cstride is the line length (in bytes) of any of the remaining
+    * Cb/Cr/CbCr planes, assumed to be the same for Cb and Cr for fully
+    * planar formats. */
+   out->strides[0] = ycbcr.ystride;
+   out->strides[1] = out->strides[2] = ycbcr.cstride;
+
+   /*
+    * Since this is EGL_NATIVE_BUFFER_ANDROID don't assume that
+    * the single-fd case cannot happen.  So handle eithe single
+    * fd or fd-per-plane case:
+    */
+   if (num_fds == 1) {
+      out->fds[1] = out->fds[0] = fds[0];
+      if (out->num_planes == 3)
+         out->fds[2] = fds[0];
+   } else {
+      assert(num_fds == out->num_planes);
+      out->fds[0] = fds[0];
+      out->fds[1] = fds[1];
+      out->fds[2] = fds[2];
+   }
+
+   return 0;
+}
+
+static int
+fallback_gralloc_get_buffer_info(struct u_gralloc *gralloc,
+                                 struct u_gralloc_buffer_handle *hnd,
+                                 struct u_gralloc_buffer_basic_info *out)
+{
+   int num_planes = 0;
+   int drm_fourcc = 0;
+   int stride = 0;
+   int fds[3];
+
+   if (is_yuv(hnd->hal_format)) {
+      int ret = fallback_gralloc_get_yuv_info(gralloc, hnd, out);
+      /*
+       * HACK: https://issuetracker.google.com/32077885
+       * There is no API available to properly query the
+       * IMPLEMENTATION_DEFINED format. As a workaround we rely here on
+       * gralloc allocating either an arbitrary YCbCr 4:2:0 or RGBX_8888, with
+       * the latter being recognized by lock_ycbcr failing.
+       */
+      if (ret != -EAGAIN)
+         return ret;
+   }
+
+   /*
+    * Non-YUV formats could *also* have multiple planes, such as ancillary
+    * color compression state buffer, but the rest of the code isn't ready
+    * yet to deal with modifiers:
+    */
+   num_planes = get_native_buffer_fds(hnd->handle, fds);
+   if (num_planes == 0)
+      return -EINVAL;
+
+   assert(num_planes == 1);
+
+   drm_fourcc = get_fourcc(hnd->hal_format);
+   if (drm_fourcc == -1) {
+      mesa_loge("Failed to get drm_fourcc");
+      return -EINVAL;
+   }
+
+   stride = hnd->pixel_stride * get_format_bpp(hnd->hal_format);
+   if (stride == 0) {
+      mesa_loge("Failed to calcuulate stride");
+      return -EINVAL;
+   }
+
+   out->drm_fourcc = drm_fourcc;
+   out->modifier = DRM_FORMAT_MOD_INVALID;
+   out->num_planes = num_planes;
+   out->fds[0] = fds[0];
+   out->strides[0] = stride;
+
+   return 0;
+}
+
+static int
+destroy(struct u_gralloc *gralloc)
+{
+   struct fallback_gralloc *gr = (struct fallback_gralloc *)gralloc;
+   if (gr->gralloc_module) {
+      dlclose(gr->gralloc_module->common.dso);
+   }
+
+   FREE(gr);
+
+   return 0;
+}
+
+struct u_gralloc *
+u_gralloc_fallback_create()
+{
+   struct fallback_gralloc *gr = CALLOC_STRUCT(fallback_gralloc);
+   int err = 0;
+
+   err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                       (const hw_module_t **)&gr->gralloc_module);
+
+   if (err) {
+      mesa_logw(
+         "No gralloc hwmodule detected (video buffers won't be supported)");
+   } else if (!gr->gralloc_module->lock_ycbcr) {
+      mesa_logw("Gralloc doesn't support lock_ycbcr (video buffers won't be "
+                "supported)");
+   }
+
+   gr->base.ops.get_buffer_basic_info = fallback_gralloc_get_buffer_info;
+   gr->base.ops.destroy = destroy;
+
+   mesa_logi("Using fallback gralloc implementation");
+
+   return &gr->base;
+}
diff --git a/src/util/u_gralloc/u_gralloc_imapper4_api.cpp b/src/util/u_gralloc/u_gralloc_imapper4_api.cpp
new file mode 100644 (file)
index 0000000..3b9380f
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2021 GlobalLogic Ukraine
+ * Copyright (C) 2021-2022 Roman Stratiienko (r.stratiienko@gmail.com)
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponent.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+#include <gralloctypes/Gralloc4.h>
+#include <system/window.h>
+
+#include "util/log.h"
+#include "u_gralloc_internal.h"
+
+using aidl::android::hardware::graphics::common::BufferUsage;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::graphics::mapper::V4_0::Error;
+using android::hardware::graphics::mapper::V4_0::IMapper;
+
+using MetadataType =
+   android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+Error
+GetMetadata(android::sp<IMapper> mapper, const native_handle_t *buffer,
+            MetadataType type, hidl_vec<uint8_t> *metadata)
+{
+   Error error = Error::NONE;
+
+   auto native_handle = const_cast<native_handle_t *>(buffer);
+
+   auto ret =
+      mapper->get(native_handle, type,
+                  [&](const auto &get_error, const auto &get_metadata) {
+                     error = get_error;
+                     *metadata = get_metadata;
+                  });
+
+   if (!ret.isOk())
+      error = Error::NO_RESOURCES;
+
+   return error;
+}
+
+std::optional<std::vector<PlaneLayout>>
+GetPlaneLayouts(android::sp<IMapper> mapper, const native_handle_t *buffer)
+{
+   hidl_vec<uint8_t> encoded_layouts;
+
+   Error error = GetMetadata(mapper, buffer,
+                             android::gralloc4::MetadataType_PlaneLayouts,
+                             &encoded_layouts);
+
+   if (error != Error::NONE)
+      return std::nullopt;
+
+   std::vector<PlaneLayout> plane_layouts;
+
+   auto status =
+      android::gralloc4::decodePlaneLayouts(encoded_layouts, &plane_layouts);
+
+   if (status != android::OK)
+      return std::nullopt;
+
+   return plane_layouts;
+}
+
+struct gralloc4 {
+   struct u_gralloc base;
+   android::sp<IMapper> mapper;
+};
+
+extern "C" {
+
+static int
+mapper4_get_buffer_basic_info(struct u_gralloc *gralloc,
+                              struct u_gralloc_buffer_handle *hnd,
+                              struct u_gralloc_buffer_basic_info *out)
+{
+   gralloc4 *gr4 = (gralloc4 *)gralloc;
+
+   if (gr4->mapper == nullptr)
+      return -EINVAL;
+
+   if (!hnd->handle)
+      return -EINVAL;
+
+   hidl_vec<uint8_t> encoded_format;
+   auto err = GetMetadata(gr4->mapper, hnd->handle,
+                          android::gralloc4::MetadataType_PixelFormatFourCC,
+                          &encoded_format);
+   if (err != Error::NONE)
+      return -EINVAL;
+
+   uint32_t drm_fourcc;
+
+   auto status =
+      android::gralloc4::decodePixelFormatFourCC(encoded_format, &drm_fourcc);
+   if (status != android::OK)
+      return -EINVAL;
+
+   uint64_t drm_modifier;
+
+   hidl_vec<uint8_t> encoded_modifier;
+   err = GetMetadata(gr4->mapper, hnd->handle,
+                     android::gralloc4::MetadataType_PixelFormatModifier,
+                     &encoded_modifier);
+   if (err != Error::NONE)
+      return -EINVAL;
+
+   status = android::gralloc4::decodePixelFormatModifier(encoded_modifier,
+                                                         &drm_modifier);
+   if (status != android::OK)
+      return -EINVAL;
+
+   out->drm_fourcc = drm_fourcc;
+   out->modifier = drm_modifier;
+
+   auto layouts_opt = GetPlaneLayouts(gr4->mapper, hnd->handle);
+
+   if (!layouts_opt)
+      return -EINVAL;
+
+   std::vector<PlaneLayout> &layouts = *layouts_opt;
+
+   out->num_planes = layouts.size();
+
+   int fd_index = 0;
+
+   for (uint32_t i = 0; i < layouts.size(); i++) {
+      out->strides[i] = layouts[i].strideInBytes;
+      out->offsets[i] = layouts[i].offsetInBytes;
+
+      /* offset == 0 means layer is located in different dma-buf */
+      if (out->offsets[i] == 0 && i > 0)
+         fd_index++;
+
+      if (fd_index >= hnd->handle->numFds)
+         return -EINVAL;
+
+      out->fds[i] = hnd->handle->data[fd_index];
+   }
+
+   return 0;
+}
+
+static int
+mapper4_get_buffer_color_info(struct u_gralloc *gralloc,
+                              struct u_gralloc_buffer_handle *hnd,
+                              struct u_gralloc_buffer_color_info *out)
+{
+   gralloc4 *gr4 = (gralloc4 *)gralloc;
+
+   if (gr4->mapper == nullptr)
+      return -EINVAL;
+
+   if (!hnd->handle)
+      return -EINVAL;
+
+   /* optional attributes */
+   hidl_vec<uint8_t> encoded_chroma_siting;
+   std::optional<ChromaSiting> chroma_siting;
+   auto err = GetMetadata(gr4->mapper, hnd->handle,
+                          android::gralloc4::MetadataType_ChromaSiting,
+                          &encoded_chroma_siting);
+   if (err == Error::NONE) {
+      ExtendableType chroma_siting_ext;
+      auto status = android::gralloc4::decodeChromaSiting(
+         encoded_chroma_siting, &chroma_siting_ext);
+      if (status != android::OK)
+         return -EINVAL;
+
+      chroma_siting =
+         android::gralloc4::getStandardChromaSitingValue(chroma_siting_ext);
+   }
+
+   hidl_vec<uint8_t> encoded_dataspace;
+   err = GetMetadata(gr4->mapper, hnd->handle,
+                     android::gralloc4::MetadataType_Dataspace,
+                     &encoded_dataspace);
+   if (err == Error::NONE) {
+      Dataspace dataspace;
+      auto status =
+         android::gralloc4::decodeDataspace(encoded_dataspace, &dataspace);
+      if (status != android::OK)
+         return -EINVAL;
+
+      Dataspace standard =
+         (Dataspace)((int)dataspace & (uint32_t)Dataspace::STANDARD_MASK);
+      switch (standard) {
+      case Dataspace::STANDARD_BT709:
+         out->yuv_color_space = __DRI_YUV_COLOR_SPACE_ITU_REC709;
+         break;
+      case Dataspace::STANDARD_BT601_625:
+      case Dataspace::STANDARD_BT601_625_UNADJUSTED:
+      case Dataspace::STANDARD_BT601_525:
+      case Dataspace::STANDARD_BT601_525_UNADJUSTED:
+         out->yuv_color_space = __DRI_YUV_COLOR_SPACE_ITU_REC601;
+         break;
+      case Dataspace::STANDARD_BT2020:
+      case Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE:
+         out->yuv_color_space = __DRI_YUV_COLOR_SPACE_ITU_REC2020;
+         break;
+      default:
+         break;
+      }
+
+      Dataspace range =
+         (Dataspace)((int)dataspace & (uint32_t)Dataspace::RANGE_MASK);
+      switch (range) {
+      case Dataspace::RANGE_FULL:
+         out->sample_range = __DRI_YUV_FULL_RANGE;
+         break;
+      case Dataspace::RANGE_LIMITED:
+         out->sample_range = __DRI_YUV_NARROW_RANGE;
+         break;
+      default:
+         break;
+      }
+   }
+
+   if (chroma_siting) {
+      switch (*chroma_siting) {
+      case ChromaSiting::SITED_INTERSTITIAL:
+         out->horizontal_siting = __DRI_YUV_CHROMA_SITING_0_5;
+         out->vertical_siting = __DRI_YUV_CHROMA_SITING_0_5;
+         break;
+      case ChromaSiting::COSITED_HORIZONTAL:
+         out->horizontal_siting = __DRI_YUV_CHROMA_SITING_0;
+         out->vertical_siting = __DRI_YUV_CHROMA_SITING_0_5;
+         break;
+      default:
+         break;
+      }
+   }
+
+   return 0;
+}
+
+static int
+mapper4_get_front_rendering_usage(struct u_gralloc *gralloc,
+                                  uint64_t *out_usage)
+{
+   assert(out_usage);
+#if ANDROID_API_LEVEL >= 33
+   *out_usage = static_cast<uint64_t>(BufferUsage::FRONT_BUFFER);
+
+   return 0;
+#else
+   return -ENOTSUP;
+#endif
+}
+
+static int
+destroy(struct u_gralloc *gralloc)
+{
+   gralloc4 *gr = (struct gralloc4 *)gralloc;
+   delete gr;
+
+   return 0;
+}
+
+struct u_gralloc *
+u_gralloc_imapper_api_create()
+{
+   auto mapper = IMapper::getService();
+   if (!mapper)
+      return NULL;
+
+   auto gr = new gralloc4;
+   gr->mapper = mapper;
+   gr->base.ops.get_buffer_basic_info = mapper4_get_buffer_basic_info;
+   gr->base.ops.get_buffer_color_info = mapper4_get_buffer_color_info;
+   gr->base.ops.get_front_rendering_usage = mapper4_get_front_rendering_usage;
+   gr->base.ops.destroy = destroy;
+
+   mesa_logi("Using IMapper v4 API");
+
+   return &gr->base;
+}
+
+} // extern "C"
diff --git a/src/util/u_gralloc/u_gralloc_internal.h b/src/util/u_gralloc/u_gralloc_internal.h
new file mode 100644 (file)
index 0000000..a837d2e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2023 Roman Stratiienko (r.stratiienko@gmail.com)
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef U_GRALLOC_INTERNAL_H
+#define U_GRALLOC_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "u_gralloc.h"
+
+struct u_gralloc_ops {
+   int (*get_buffer_basic_info)(struct u_gralloc *gralloc,
+                                struct u_gralloc_buffer_handle *hnd,
+                                struct u_gralloc_buffer_basic_info *out);
+   int (*get_buffer_color_info)(struct u_gralloc *gralloc,
+                                struct u_gralloc_buffer_handle *hnd,
+                                struct u_gralloc_buffer_color_info *out);
+   int (*get_front_rendering_usage)(struct u_gralloc *gralloc,
+                                    uint64_t *out_usage);
+   int (*destroy)(struct u_gralloc *gralloc);
+};
+
+struct u_gralloc {
+   struct u_gralloc_ops ops;
+};
+
+extern struct u_gralloc *u_gralloc_cros_api_create(void);
+#ifdef USE_IMAPPER4_METADATA_API
+extern struct u_gralloc *u_gralloc_imapper_api_create(void);
+#endif
+extern struct u_gralloc *u_gralloc_fallback_create(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* U_GRALLOC_INTERNAL_H */