[PATCH mesa 3/3] Adapt the EGL_WL_create_wayland_buffer_from_image extension for YUV

Neil Roberts neil at linux.intel.com
Thu Feb 6 05:58:31 PST 2014


The eglCreateWaylandBufferFromImageWL function in the extension is modified so
that instead of just taking a single image it can now take a set of images.
Each EGLImage will represent a plane from a planar image. The overall format
for the combined set of images isn't stored in the EGLImages so instead of
trying to deduce it the function now also has a format parameter. This should
contain the EGLenum returned in the EGL_TEXTURE_FORMAT attribute retreived via
eglQueryWaylandBufferWL. The exact wl_drm_format value to use is deduced from
the EGL format and the size of the images. The number of images passed must
match the number of planes required by the texture format. The EGLImages must
all point to the same buffer and this is determined by checking the names of
the images. If any of these criteria fail then the function will report
EGL_BAD_MATCH.
---
 .../specs/WL_create_wayland_buffer_from_image.spec |  75 +++++--
 include/EGL/eglmesaext.h                           |   4 +-
 src/egl/drivers/dri2/platform_wayland.c            | 236 ++++++++++++++++++---
 src/egl/main/eglapi.c                              |  25 ++-
 src/egl/main/eglapi.h                              |   2 +-
 5 files changed, 283 insertions(+), 59 deletions(-)

diff --git a/docs/specs/WL_create_wayland_buffer_from_image.spec b/docs/specs/WL_create_wayland_buffer_from_image.spec
index aa5eb4d..6595f1a 100644
--- a/docs/specs/WL_create_wayland_buffer_from_image.spec
+++ b/docs/specs/WL_create_wayland_buffer_from_image.spec
@@ -22,7 +22,7 @@ Status
 
 Version
 
-    Version 2, October 25, 2013
+    Version 3, February 5, 2014
 
 Number
 
@@ -35,20 +35,22 @@ Dependencies
 
     EGL_KHR_base_image is required.
 
+    EGL_WL_bind_wayland_display affects the definition of this extension.
+
 Overview
 
     This extension provides an entry point to create a wl_buffer which shares
-    its contents with a given EGLImage. The expected use case for this is in a
-    nested Wayland compositor which is using subsurfaces to present buffers
-    from its clients. Using this extension it can attach the client buffers
-    directly to the subsurface without having to blit the contents into an
-    intermediate buffer. The compositing can then be done in the parent
-    compositor.
-
-    The nested compositor can create an EGLImage from a client buffer resource
-    using the existing WL_bind_wayland_display extension. It should also be
-    possible to create buffers using other types of images although there is
-    no expected use case for that.
+    its contents with a given set of EGLImages. The expected use case for this
+    is in a nested Wayland compositor which is using subsurfaces to present
+    buffers from its clients. Using this extension it can attach the client
+    buffers directly to the subsurface without having to blit the contents
+    into an intermediate buffer. The compositing can then be done in the
+    parent compositor.
+
+    The nested compositor can create the EGLImages from a client buffer
+    resource using the existing WL_bind_wayland_display extension. It should
+    also be possible to create buffers using other types of images although
+    there is no expected use case for that.
 
 IP Status
 
@@ -57,7 +59,9 @@ IP Status
 New Procedures and Functions
 
     struct wl_buffer *eglCreateWaylandBufferFromImageWL(EGLDisplay dpy,
-                                                        EGLImageKHR image);
+                                                        EGLenum format,
+                                                        EGLImageKHR *images,
+                                                        EGLint n_images);
 
 New Tokens
 
@@ -68,27 +72,57 @@ Additions to the EGL 1.4 Specification:
     To create a client-side wl_buffer from an EGLImage call
 
       struct wl_buffer *eglCreateWaylandBufferFromImageWL(EGLDisplay dpy,
-                                                          EGLImageKHR image);
+                                                          EGLenum format,
+                                                          EGLImageKHR *images,
+                                                          EGLint n_images);
 
-    The returned buffer will share the contents with the given EGLImage. Any
-    updates to the image will also be updated in the wl_buffer. Typically the
-    EGLImage will be generated in a nested Wayland compositor using a buffer
+    The returned buffer will share the contents with the given EGLImages. Any
+    updates to the images will also be updated in the wl_buffer. Typically the
+    EGLImages will be generated in a nested Wayland compositor using a buffer
     resource from a client via the EGL_WL_bind_wayland_display extension.
 
+    The number of images passed should match the number of planes required for
+    the format given in the format parameter. For non-planar formats this will
+    just be 1. Each image represents one plane within the buffer.
+
+    The formats accepted in the format parameter are:
+
+       EGL_TEXTURE_RGB        A single image is used which contains RGB
+                              components.
+       EGL_TEXTURE_RGBA       A single image is used which contains RGBA
+                              components.
+       EGL_TEXTURE_Y_U_V_WL   Three images are used; one containing the
+                              luminance component, one containing the
+                              U component of the chrominance data and another
+                              containing the V component.
+       EGL_TEXTURE_Y_UV_WL    Two images are used; one containing a single
+                              luminance component and the other containing the
+                              two chrominance components.
+       EGL_TEXTURE_Y_XUXV_WL  Two images are used; one containing a single
+                              luminance component and the other containing the
+                              two chrominance components along with two unused
+                              components.
+
     If there was an error then the function will return NULL. In particular it
     will generate EGL_BAD_MATCH if the implementation is not able to represent
-    the image as a wl_buffer. The possible reasons for this error are
+    the images as a wl_buffer. The possible reasons for this error are
     implementation-dependant but may include problems such as an unsupported
     format or tiling mode or that the buffer is in memory that is inaccessible
     to the GPU that the given EGLDisplay is using.
 
+Dependencies on EGL_WL_bind_wayland_display
+
+    If EGL_WL_bind_wayland_display is not implemented then references to
+    EGL_TEXTURE_Y_U_V_WL, EGL_TEXTURE_Y_UV_WL and EGL_TEXTURE_Y_XUXV_WL are
+    void.
+
 Issues
 
     1) Under what circumstances can the EGL_BAD_MATCH error be generated? Does
        this include for example unsupported tiling modes?
 
        RESOLVED: Yes, the EGL_BAD_MATCH error can be generated for any reason
-       which prevents the implementation from representing the image as a
+       which prevents the implementation from representing the images as a
        wl_buffer. For example, these problems can be but are not limited to
        unsupported tiling modes, inaccessible memory or an unsupported pixel
        format.
@@ -99,3 +133,6 @@ Revision History
         Initial draft (Neil Roberts)
     Version 2, October 25, 2013
         Added a note about more possible reasons for returning EGL_BAD_FORMAT.
+    Version 3, February 5, 2014
+        Support for passing multiple images in order to create a planar
+        buffer.
diff --git a/include/EGL/eglmesaext.h b/include/EGL/eglmesaext.h
index b3229e3..de90369 100644
--- a/include/EGL/eglmesaext.h
+++ b/include/EGL/eglmesaext.h
@@ -138,9 +138,9 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, st
 #define EGL_WL_create_wayland_buffer_from_image 1
 
 #ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
+EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLenum format, EGLImageKHR *images, EGLint n_images);
 #endif
-typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
+typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLenum format, EGLImageKHR *images, EGLint n_images);
 
 #endif
 
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index 089ed62..e9262fb 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -698,64 +698,238 @@ dri2_can_handle_format(struct dri2_egl_display *dri2_dpy,
    return EGL_FALSE;
 }
 
+struct plane_attribs {
+   struct dri2_egl_image *dri2_img;
+   __DRIimage *image;
+   int offset;
+   int stride;
+   int width;
+   int height;
+   int format;
+};
+
+struct planar_format {
+   enum wl_drm_format wl_format;
+   EGLenum components;
+   int n_planes;
+   struct {
+      int width_shift;
+      int height_shift;
+      uint32_t dri_format;
+   } planes[3];
+};
+
+static const struct planar_format
+planar_formats[] = {
+   { WL_DRM_FORMAT_ARGB8888, EGL_TEXTURE_RGBA, 1,
+     { { 0, 0, __DRI_IMAGE_FORMAT_ARGB8888 } } },
+
+   { WL_DRM_FORMAT_XRGB8888, EGL_TEXTURE_RGB, 1,
+     { { 0, 0, __DRI_IMAGE_FORMAT_XRGB8888 }, } },
+
+   { WL_DRM_FORMAT_RGB565, EGL_TEXTURE_RGB, 1,
+     { { 0, 0, __DRI_IMAGE_FORMAT_RGB565 } } },
+
+   { WL_DRM_FORMAT_YUV410, EGL_TEXTURE_Y_U_V_WL, 3,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 2, 2, __DRI_IMAGE_FORMAT_R8 },
+       { 2, 2, __DRI_IMAGE_FORMAT_R8 } } },
+
+   { WL_DRM_FORMAT_YUV411, EGL_TEXTURE_Y_U_V_WL, 3,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 2, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 2, 0, __DRI_IMAGE_FORMAT_R8 } } },
+
+   { WL_DRM_FORMAT_YUV420, EGL_TEXTURE_Y_U_V_WL, 3,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 1, 1, __DRI_IMAGE_FORMAT_R8 },
+       { 1, 1, __DRI_IMAGE_FORMAT_R8 } } },
+
+   { WL_DRM_FORMAT_YUV422, EGL_TEXTURE_Y_U_V_WL, 3,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 1, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 1, 0, __DRI_IMAGE_FORMAT_R8 } } },
+
+   { WL_DRM_FORMAT_YUV444, EGL_TEXTURE_Y_U_V_WL, 3,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 0, 0, __DRI_IMAGE_FORMAT_R8 } } },
+
+   { WL_DRM_FORMAT_NV12, EGL_TEXTURE_Y_UV_WL, 2,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 1, 1, __DRI_IMAGE_FORMAT_GR88 } } },
+
+   { WL_DRM_FORMAT_NV16, EGL_TEXTURE_Y_UV_WL, 2,
+     { { 0, 0, __DRI_IMAGE_FORMAT_R8 },
+       { 1, 0, __DRI_IMAGE_FORMAT_GR88 } } },
+
+   { WL_DRM_FORMAT_YUYV, EGL_TEXTURE_Y_XUXV_WL, 2,
+     { { 0, 0, __DRI_IMAGE_FORMAT_GR88 },
+       { 1, 0, __DRI_IMAGE_FORMAT_ARGB8888 } } }
+};
+
+static void
+get_planar_format_max_shifts(const struct planar_format *planar_format,
+                             int *max_width_shift,
+                             int *max_height_shift)
+{
+   int i;
+
+   *max_width_shift = 0;
+   *max_height_shift = 0;
+
+   for (i = 0; i < planar_format->n_planes; i++) {
+      if (planar_format->planes[i].width_shift > *max_width_shift)
+         *max_width_shift = planar_format->planes[i].width_shift;
+      if (planar_format->planes[i].height_shift > *max_height_shift)
+         *max_height_shift = planar_format->planes[i].height_shift;
+   }
+}
+
+static EGLBoolean
+check_plane_attribs_match_format(const struct plane_attribs *planes,
+                                 const struct planar_format *planar_format)
+{
+   int width, height;
+   int i;
+
+   for (i = 0; i < planar_format->n_planes; i++) {
+      if (planes[i].format != planar_format->planes[i].dri_format)
+         return EGL_FALSE;
+
+      width = planes[0].width >> planar_format->planes[i].width_shift;
+      height = planes[0].height >> planar_format->planes[i].height_shift;
+
+      if (planes[i].width != width || planes[i].height != height)
+         return EGL_FALSE;
+   }
+
+   return EGL_TRUE;
+}
+
+static EGLBoolean
+find_planar_format(EGLenum components,
+                   const struct plane_attribs *planes,
+                   int n_planes,
+                   enum wl_drm_format *wl_format)
+{
+   const struct planar_format *planar_format;
+   int max_width_shift, max_height_shift;
+   int i;
+
+   for (i = 0; i < ARRAY_SIZE(planar_formats); i++) {
+      planar_format = planar_formats + i;
+
+      if (n_planes != planar_format->n_planes ||
+          components != planar_format->components)
+         continue;
+
+      /* Check if the size of the first plane is a multiple of the maximum
+       * shift */
+      get_planar_format_max_shifts(planar_format,
+                                   &max_width_shift,
+                                   &max_height_shift);
+      if ((planes[0].width & ((1 << max_width_shift) - 1)) != 0 ||
+          (planes[0].height & ((1 << max_height_shift) - 1)) != 0)
+         continue;
+
+      if (!check_plane_attribs_match_format(planes, planar_format))
+         continue;
+
+      *wl_format = planar_format->wl_format;
+      return EGL_TRUE;
+   }
+
+   return EGL_FALSE;
+}
+
 static struct wl_buffer *
 dri2_create_wayland_buffer_from_image_wl(_EGLDriver *drv,
                                          _EGLDisplay *disp,
-                                         _EGLImage *img)
+                                         EGLenum format,
+                                         _EGLImage **images,
+                                         EGLint n_images)
 {
    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
-   struct dri2_egl_image *dri2_img = dri2_egl_image(img);
-   __DRIimage *image = dri2_img->dri_image;
    struct wl_buffer *buffer;
-   int width, height, format, pitch;
    enum wl_drm_format wl_format;
+   struct plane_attribs plane[3];
+   int name, other_name;
+   int i;
 
-   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
+   if (n_images < 1 || n_images > 3)
+      goto bad_format;
 
-   switch (format) {
-   case __DRI_IMAGE_FORMAT_ARGB8888:
-      if (!dri2_can_handle_format(dri2_dpy, WL_DRM_FORMAT_ARGB8888))
-         goto bad_format;
-      wl_format = WL_DRM_FORMAT_ARGB8888;
-      break;
-   case __DRI_IMAGE_FORMAT_XRGB8888:
-      if (!dri2_can_handle_format(dri2_dpy, WL_DRM_FORMAT_XRGB8888))
+   for (i = 0; i < n_images; i++) {
+      plane[i].dri2_img = dri2_egl_image(images[i]);
+      plane[i].image = plane[i].dri2_img->dri_image;
+   }
+
+   /* Verify the all of the images have the same name (ie, they point to the
+    * same buffer) */
+   dri2_dpy->image->queryImage(plane[0].image, __DRI_IMAGE_ATTRIB_NAME, &name);
+   for (i = 1; i < n_images; i++) {
+      dri2_dpy->image->queryImage(plane[i].image,
+                                  __DRI_IMAGE_ATTRIB_NAME,
+                                  &other_name);
+      if (other_name != name)
          goto bad_format;
-      wl_format = WL_DRM_FORMAT_XRGB8888;
-      break;
-   default:
-      goto bad_format;
    }
 
-   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_WIDTH, &width);
-   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HEIGHT, &height);
-   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch);
+   for (i = 0; i < n_images; i++) {
+      dri2_dpy->image->queryImage(plane[i].image,
+                                  __DRI_IMAGE_ATTRIB_FORMAT,
+                                  &plane[i].format);
+      dri2_dpy->image->queryImage(plane[i].image,
+                                  __DRI_IMAGE_ATTRIB_WIDTH,
+                                  &plane[i].width);
+      dri2_dpy->image->queryImage(plane[i].image,
+                                  __DRI_IMAGE_ATTRIB_HEIGHT,
+                                  &plane[i].height);
+      dri2_dpy->image->queryImage(plane[i].image,
+                                  __DRI_IMAGE_ATTRIB_STRIDE,
+                                  &plane[i].stride);
+
+      if (dri2_dpy->image->base.version >= 9)
+         dri2_dpy->image->queryImage(plane[i].image,
+                                     __DRI_IMAGE_ATTRIB_OFFSET,
+                                     &plane[i].offset);
+      else
+         plane[i].offset = 0;
+   }
+
+   /* We need prime to create planar buffers */
+   if (n_images > 1 &&
+       (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) == 0)
+      goto bad_format;
+
+   if (!find_planar_format(format, plane, n_images, &wl_format))
+      goto bad_format;
+
+   if (!dri2_can_handle_format(dri2_dpy, wl_format))
+      goto bad_format;
 
    if (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) {
       int fd;
 
-      dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD, &fd);
+      dri2_dpy->image->queryImage(plane[0].image, __DRI_IMAGE_ATTRIB_FD, &fd);
 
       buffer =
          wl_drm_create_prime_buffer(dri2_dpy->wl_drm,
                                     fd,
-                                    width, height,
+                                    plane[0].width, plane[0].height,
                                     wl_format,
-                                    0, pitch,
-                                    0, 0,
-                                    0, 0);
+                                    plane[0].offset, plane[0].stride,
+                                    plane[1].offset, plane[1].stride,
+                                    plane[2].offset, plane[2].stride);
 
       close(fd);
    } else {
-      int name;
-
-      dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NAME, &name);
-
       buffer =
          wl_drm_create_buffer(dri2_dpy->wl_drm,
                               name,
-                              width, height,
-                              pitch,
+                              plane[0].width, plane[0].height,
+                              plane[0].stride,
                               wl_format);
    }
 
diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
index 59e214c..a5b3f91 100644
--- a/src/egl/main/eglapi.c
+++ b/src/egl/main/eglapi.c
@@ -1606,22 +1606,35 @@ eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer,
 
 #ifdef EGL_WL_create_wayland_buffer_from_image
 struct wl_buffer * EGLAPIENTRY
-eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image)
+eglCreateWaylandBufferFromImageWL(EGLDisplay dpy,
+                                  EGLenum format,
+                                  EGLImageKHR *images,
+                                  EGLint n_images)
 {
    _EGLDisplay *disp = _eglLockDisplay(dpy);
-   _EGLImage *img;
+   _EGLImage *imgs[3];
    _EGLDriver *drv;
    struct wl_buffer *ret;
+   int i;
 
    _EGL_CHECK_DISPLAY(disp, NULL, drv);
    assert(disp->Extensions.WL_create_wayland_buffer_from_image);
 
-   img = _eglLookupImage(image, disp);
-
-   if (!img)
+   if (n_images < 0 || n_images > ARRAY_SIZE(imgs))
       RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, NULL);
 
-   ret = drv->API.CreateWaylandBufferFromImageWL(drv, disp, img);
+   for (i = 0; i < n_images; i++) {
+      imgs[i] = _eglLookupImage(images[i], disp);
+
+      if (!imgs[i])
+         RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, NULL);
+   }
+
+   ret = drv->API.CreateWaylandBufferFromImageWL(drv,
+                                                 disp,
+                                                 format,
+                                                 imgs,
+                                                 n_images);
 
    RETURN_EGL_EVAL(disp, ret);
 }
diff --git a/src/egl/main/eglapi.h b/src/egl/main/eglapi.h
index 091c09c..2a4f78a 100644
--- a/src/egl/main/eglapi.h
+++ b/src/egl/main/eglapi.h
@@ -127,7 +127,7 @@ typedef EGLBoolean (*QueryWaylandBufferWL_t)(_EGLDriver *drv, _EGLDisplay *displ
 #endif
 
 #ifdef EGL_WL_create_wayland_buffer_from_image
-typedef struct wl_buffer * (*CreateWaylandBufferFromImageWL_t)(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img);
+typedef struct wl_buffer * (*CreateWaylandBufferFromImageWL_t)(_EGLDriver *drv, _EGLDisplay *disp, EGLenum format, _EGLImage **images, EGLint n_images);
 #endif
 
 typedef EGLBoolean (*PostSubBufferNV_t)(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surface, EGLint x, EGLint y, EGLint width, EGLint height);
-- 
1.8.5.3



More information about the wayland-devel mailing list