egl/android: Use gralloc::lock_ycbcr for resolving YUV formats (v2)
authorTomasz Figa <tfiga@chromium.org>
Thu, 10 Nov 2016 07:55:53 +0000 (16:55 +0900)
committerEmil Velikov <emil.l.velikov@gmail.com>
Mon, 21 Nov 2016 13:27:47 +0000 (13:27 +0000)
There is an interface that can be used to query YUV buffers for their
internal format. Specifically, if gralloc:lock_ycbcr() is given no SW
usage flags, it's supposed to return plane offsets instead of pointers.
Let's use this interface to implement support for YUV formats in Android
EGL backend.

v2: Fixes from Emil's review:
 a) Added comments for parts that might be not clear,
 b) Changed get_fourcc_yuv() to return -1 on failure,
 c) Changed is_yuv() to use bool.

Signed-off-by: Tomasz Figa <tfiga@chromium.org>
Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
Reviewed-by: Tapani Pälli <tapani.palli@intel.com>
src/egl/drivers/dri2/platform_android.c

index fc499327d63b45208b0b117be69bf21dea5c8645..373e2c04322bcb030630057b259b3ff22cfaeb2e 100644 (file)
@@ -31,6 +31,7 @@
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <xf86drm.h>
+#include <stdbool.h>
 
 #if ANDROID_VERSION >= 0x402
 #include <sync/sync.h>
 
 #define ALIGN(val, align)      (((val) + (align) - 1) & ~((align) - 1))
 
+struct droid_yuv_format {
+   /* Lookup keys */
+   int native; /* HAL_PIXEL_FORMAT_ */
+   int is_ycrcb; /* 0 if chroma order is {Cb, Cr}, 1 if {Cr, Cb} */
+   int chroma_step; /* Distance in bytes between subsequent chroma pixels. */
+
+   /* Result */
+   int fourcc; /* __DRI_IMAGE_FOURCC_ */
+};
+
+/* 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,   0, 2, __DRI_IMAGE_FOURCC_NV12 },
+   { HAL_PIXEL_FORMAT_YCbCr_420_888,   0, 1, __DRI_IMAGE_FOURCC_YUV420 },
+   { HAL_PIXEL_FORMAT_YCbCr_420_888,   1, 1, __DRI_IMAGE_FOURCC_YVU420 },
+   { HAL_PIXEL_FORMAT_YV12,            1, 1, __DRI_IMAGE_FOURCC_YVU420 },
+};
+
+static int
+get_fourcc_yuv(int native, int is_ycrcb, int chroma_step)
+{
+   int i;
+
+   for (i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
+      if (droid_yuv_formats[i].native == native &&
+          droid_yuv_formats[i].is_ycrcb == is_ycrcb &&
+          droid_yuv_formats[i].chroma_step == chroma_step)
+         return droid_yuv_formats[i].fourcc;
+
+   return -1;
+}
+
+static bool
+is_yuv(int native)
+{
+   int i;
+
+   for (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)
 {
@@ -57,9 +104,6 @@ get_format_bpp(int native)
    case HAL_PIXEL_FORMAT_RGB_565:
       bpp = 2;
       break;
-   case HAL_PIXEL_FORMAT_YV12:
-      bpp = 1;
-      break;
    default:
       bpp = 0;
       break;
@@ -76,7 +120,6 @@ static int get_fourcc(int native)
    case HAL_PIXEL_FORMAT_BGRA_8888: return __DRI_IMAGE_FOURCC_ARGB8888;
    case HAL_PIXEL_FORMAT_RGBA_8888: return __DRI_IMAGE_FOURCC_ABGR8888;
    case HAL_PIXEL_FORMAT_RGBX_8888: return __DRI_IMAGE_FOURCC_XBGR8888;
-   case HAL_PIXEL_FORMAT_YV12:      return __DRI_IMAGE_FOURCC_YVU420;
    default:
       _eglLog(_EGL_WARNING, "unsupported native buffer format 0x%x", native);
    }
@@ -541,35 +584,80 @@ droid_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
 }
 
 static _EGLImage *
-droid_create_image_from_prime_fd(_EGLDisplay *disp, _EGLContext *ctx,
-                                 struct ANativeWindowBuffer *buf, int fd)
+droid_create_image_from_prime_fd_yuv(_EGLDisplay *disp, _EGLContext *ctx,
+                                     struct ANativeWindowBuffer *buf, int fd)
 {
-   unsigned int offsets[3] = { 0, 0, 0 };
-   unsigned int pitches[3] = { 0, 0, 0 };
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   struct android_ycbcr ycbcr;
+   size_t offsets[3];
+   size_t pitches[3];
+   int is_ycrcb;
+   int fourcc;
+   int ret;
 
-   const int fourcc = get_fourcc(buf->format);
-   if (fourcc == -1) {
-      _eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR");
+   if (!dri2_dpy->gralloc->lock_ycbcr) {
+      _eglLog(_EGL_WARNING, "Gralloc does not support lock_ycbcr");
       return NULL;
    }
 
-   pitches[0] = buf->stride * get_format_bpp(buf->format);
-   if (pitches[0] == 0) {
-      _eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR");
+   memset(&ycbcr, 0, sizeof(ycbcr));
+   ret = dri2_dpy->gralloc->lock_ycbcr(dri2_dpy->gralloc, buf->handle,
+                                       0, 0, 0, 0, 0, &ycbcr);
+   if (ret) {
+      _eglLog(_EGL_WARNING, "gralloc->lock_ycbcr failed: %d", ret);
+      return NULL;
+   }
+   dri2_dpy->gralloc->unlock(dri2_dpy->gralloc, buf->handle);
+
+   /* 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. */
+   offsets[0] = (size_t)ycbcr.y;
+   /* We assume here that all the planes are located in one DMA-buf. */
+   is_ycrcb = (size_t)ycbcr.cb < (size_t)ycbcr.cr;
+   if (is_ycrcb) {
+      offsets[1] = (size_t)ycbcr.cr;
+      offsets[2] = (size_t)ycbcr.cb;
+   } else {
+      offsets[1] = (size_t)ycbcr.cb;
+      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. */
+   pitches[0] = ycbcr.ystride;
+   pitches[1] = pitches[2] = ycbcr.cstride;
+
+   /* .chroma_step is the byte distance between the same chroma channel
+    * values of subsequent pixels, assumed to be the same for Cb and Cr. */
+   fourcc = get_fourcc_yuv(buf->format, is_ycrcb, ycbcr.chroma_step);
+   if (fourcc == -1) {
+      _eglLog(_EGL_WARNING, "unsupported YUV format, native = %x, is_ycrcb = %d, chroma_step = %d",
+              buf->format, is_ycrcb, ycbcr.chroma_step);
       return NULL;
    }
 
-   switch (buf->format) {
-   case HAL_PIXEL_FORMAT_YV12:
-      /* Y plane is assumed to be at offset 0. */
-      /* Cr plane is located after Y plane */
-      offsets[1] = offsets[0] + pitches[0] * buf->height;
-      pitches[1] = ALIGN(pitches[0] / 2, 16);
-      /* Cb plane is located after Cr plane */
-      offsets[2] = offsets[1] + pitches[1] * buf->height / 2;
-      pitches[2] = pitches[1];
-
-      const EGLint attr_list_yv12[] = {
+   if (ycbcr.chroma_step == 2) {
+      /* Semi-planar Y + CbCr or Y + CbCr format. */
+      const EGLint attr_list_2plane[] = {
+         EGL_WIDTH, buf->width,
+         EGL_HEIGHT, buf->height,
+         EGL_LINUX_DRM_FOURCC_EXT, fourcc,
+         EGL_DMA_BUF_PLANE0_FD_EXT, fd,
+         EGL_DMA_BUF_PLANE0_PITCH_EXT, pitches[0],
+         EGL_DMA_BUF_PLANE0_OFFSET_EXT, offsets[0],
+         EGL_DMA_BUF_PLANE1_FD_EXT, fd,
+         EGL_DMA_BUF_PLANE1_PITCH_EXT, pitches[1],
+         EGL_DMA_BUF_PLANE1_OFFSET_EXT, offsets[1],
+         EGL_NONE, 0
+      };
+
+      return dri2_create_image_dma_buf(disp, ctx, NULL, attr_list_2plane);
+   } else {
+      /* Fully planar Y + Cb + Cr or Y + Cr + Cb format. */
+      const EGLint attr_list_3plane[] = {
          EGL_WIDTH, buf->width,
          EGL_HEIGHT, buf->height,
          EGL_LINUX_DRM_FOURCC_EXT, fourcc,
@@ -585,7 +673,29 @@ droid_create_image_from_prime_fd(_EGLDisplay *disp, _EGLContext *ctx,
          EGL_NONE, 0
       };
 
-      return dri2_create_image_dma_buf(disp, ctx, NULL, attr_list_yv12);
+      return dri2_create_image_dma_buf(disp, ctx, NULL, attr_list_3plane);
+   }
+}
+
+static _EGLImage *
+droid_create_image_from_prime_fd(_EGLDisplay *disp, _EGLContext *ctx,
+                                 struct ANativeWindowBuffer *buf, int fd)
+{
+   unsigned int pitch;
+
+   if (is_yuv(buf->format))
+      return droid_create_image_from_prime_fd_yuv(disp, ctx, buf, fd);
+
+   const int fourcc = get_fourcc(buf->format);
+   if (fourcc == -1) {
+      _eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR");
+      return NULL;
+   }
+
+   pitch = buf->stride * get_format_bpp(buf->format);
+   if (pitch == 0) {
+      _eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR");
+      return NULL;
    }
 
    const EGLint attr_list[] = {
@@ -593,7 +703,7 @@ droid_create_image_from_prime_fd(_EGLDisplay *disp, _EGLContext *ctx,
       EGL_HEIGHT, buf->height,
       EGL_LINUX_DRM_FOURCC_EXT, fourcc,
       EGL_DMA_BUF_PLANE0_FD_EXT, fd,
-      EGL_DMA_BUF_PLANE0_PITCH_EXT, pitches[0],
+      EGL_DMA_BUF_PLANE0_PITCH_EXT, pitch,
       EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
       EGL_NONE, 0
    };