From 51727b1cf57e8c4630767eb9ead207b102ffa489 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 10 Nov 2016 16:55:53 +0900 Subject: [PATCH] egl/android: Use gralloc::lock_ycbcr for resolving YUV formats (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Emil Velikov Reviewed-by: Tapani Pälli --- src/egl/drivers/dri2/platform_android.c | 166 ++++++++++++++++++++---- 1 file changed, 138 insertions(+), 28 deletions(-) diff --git a/src/egl/drivers/dri2/platform_android.c b/src/egl/drivers/dri2/platform_android.c index fc499327d63..373e2c04322 100644 --- a/src/egl/drivers/dri2/platform_android.c +++ b/src/egl/drivers/dri2/platform_android.c @@ -31,6 +31,7 @@ #include #include #include +#include #if ANDROID_VERSION >= 0x402 #include @@ -43,6 +44,52 @@ #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 (!dri2_dpy->gralloc->lock_ycbcr) { + _eglLog(_EGL_WARNING, "Gralloc does not support lock_ycbcr"); + return NULL; + } + + 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) { - _eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR"); + _eglLog(_EGL_WARNING, "unsupported YUV format, native = %x, is_ycrcb = %d, chroma_step = %d", + buf->format, is_ycrcb, ycbcr.chroma_step); return NULL; } - pitches[0] = buf->stride * get_format_bpp(buf->format); - if (pitches[0] == 0) { - _eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR"); - return NULL; - } + 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 + }; - 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[] = { + 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 };