egl/wayland: Use linux-dmabuf interface for buffers
authorDaniel Stone <daniels@collabora.com>
Fri, 16 Jun 2017 17:01:23 +0000 (18:01 +0100)
committerDaniel Stone <daniels@collabora.com>
Tue, 18 Jul 2017 21:16:21 +0000 (22:16 +0100)
When available, use the zwp_linux_dambuf_v1 interface to create buffers,
which allows multiple planes and buffer modifiers to be used.

Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
configure.ac
src/egl/Makefile.am
src/egl/drivers/dri2/.gitignore [new file with mode: 0644]
src/egl/drivers/dri2/egl_dri2.c
src/egl/drivers/dri2/egl_dri2.h
src/egl/drivers/dri2/platform_wayland.c

index 46fcd8f3fe78b9f2eb6dfa3f99d1c79bfc6e8b03..c5803e0f6ecdb0987ac724804f4d8dc65514dae2 100644 (file)
@@ -88,6 +88,7 @@ LIBOMXIL_BELLAGIO_REQUIRED=0.0
 LIBVA_REQUIRED=0.38.0
 VDPAU_REQUIRED=1.1
 WAYLAND_REQUIRED=1.11
+WAYLAND_PROTOCOLS_REQUIRED=1.8
 XCB_REQUIRED=1.9.3
 XCBDRI2_REQUIRED=1.8
 XCBGLX_REQUIRED=1.8.1
@@ -1686,7 +1687,9 @@ for plat in $platforms; do
        case "$plat" in
        wayland)
 
-               PKG_CHECK_MODULES([WAYLAND], [wayland-client >= $WAYLAND_REQUIRED wayland-server >= $WAYLAND_REQUIRED])
+               PKG_CHECK_MODULES([WAYLAND], [wayland-client >= $WAYLAND_REQUIRED wayland-server >= $WAYLAND_REQUIRED wayland-protocols >= $WAYLAND_PROTOCOLS_REQUIRED])
+                ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`
+                AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
 
                if test "x$WAYLAND_SCANNER" = "x:"; then
                        AC_MSG_ERROR([wayland-scanner is needed to compile the wayland platform])
index 81090387b582f65b6e3a98210496550a7c773423..19295de3ed67fb200aba476df0029e81064ec359 100644 (file)
@@ -21,6 +21,8 @@
 
 include Makefile.sources
 
+BUILT_SOURCES =
+
 AM_CFLAGS = \
        -I$(top_srcdir)/include \
        -I$(top_srcdir)/src/egl/main \
@@ -61,11 +63,27 @@ endif
 endif
 
 if HAVE_PLATFORM_WAYLAND
+WL_DMABUF_XML = $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
+
+drivers/dri2/linux-dmabuf-unstable-v1-protocol.c: $(WL_DMABUF_XML)
+       $(MKDIR_GEN)
+       $(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
+
+drivers/dri2/linux-dmabuf-unstable-v1-client-protocol.h: $(WL_DMABUF_XML)
+       $(MKDIR_GEN)
+       $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
+
+BUILT_SOURCES += \
+       drivers/dri2/linux-dmabuf-unstable-v1-protocol.c \
+       drivers/dri2/linux-dmabuf-unstable-v1-client-protocol.h
+
 AM_CFLAGS += $(WAYLAND_CFLAGS)
 libEGL_common_la_LIBADD += $(WAYLAND_LIBS)
 libEGL_common_la_LIBADD += $(LIBDRM_LIBS)
 libEGL_common_la_LIBADD += $(top_builddir)/src/egl/wayland/wayland-drm/libwayland-drm.la
-dri2_backend_FILES += drivers/dri2/platform_wayland.c
+libEGL_common_la_LIBADD += $(top_builddir)/src/util/libmesautil.la
+dri2_backend_FILES += drivers/dri2/platform_wayland.c  \
+       drivers/dri2/linux-dmabuf-unstable-v1-protocol.c
 endif
 
 if HAVE_PLATFORM_DRM
@@ -85,6 +103,7 @@ endif
 
 AM_CFLAGS += \
        -I$(top_srcdir)/src/loader \
+       -I$(top_builddir)/src/egl/drivers/dri2 \ 
        -I$(top_srcdir)/src/egl/drivers/dri2 \
        -I$(top_srcdir)/src/gbm/backends/dri \
        -I$(top_srcdir)/src/egl/wayland/wayland-egl \
@@ -118,7 +137,7 @@ g_egldispatchstubs.h: $(GLVND_GEN_DEPS)
                $(top_srcdir)/src/egl/generate/egl.xml \
                $(top_srcdir)/src/egl/generate/egl_other.xml > $@
 
-BUILT_SOURCES = g_egldispatchstubs.c g_egldispatchstubs.h
+BUILT_SOURCES += g_egldispatchstubs.c g_egldispatchstubs.h
 CLEANFILES = $(BUILT_SOURCES)
 
 if USE_LIBGLVND
diff --git a/src/egl/drivers/dri2/.gitignore b/src/egl/drivers/dri2/.gitignore
new file mode 100644 (file)
index 0000000..e96becb
--- /dev/null
@@ -0,0 +1,2 @@
+linux-dmabuf-unstable-v1-client-protocol.h
+linux-dmabuf-unstable-v1-protocol.c
index 072494ed4ed9f36d7a05dfacbc9808f058cf817e..a197e0456fd2536d37d1b641041d73d7f8fe9b06 100644 (file)
@@ -53,6 +53,7 @@
 #ifdef HAVE_WAYLAND_PLATFORM
 #include "wayland-drm.h"
 #include "wayland-drm-client-protocol.h"
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
 #endif
 
 #ifdef HAVE_X11_PLATFORM
@@ -62,6 +63,7 @@
 #include "egl_dri2.h"
 #include "loader/loader.h"
 #include "util/u_atomic.h"
+#include "util/u_vector.h"
 
 /* The kernel header drm_fourcc.h defines the DRM formats below.  We duplicate
  * some of the definitions here so that building Mesa won't bleeding-edge
@@ -938,11 +940,16 @@ dri2_display_destroy(_EGLDisplay *disp)
    case _EGL_PLATFORM_WAYLAND:
       if (dri2_dpy->wl_drm)
           wl_drm_destroy(dri2_dpy->wl_drm);
+      if (dri2_dpy->wl_dmabuf)
+          zwp_linux_dmabuf_v1_destroy(dri2_dpy->wl_dmabuf);
       if (dri2_dpy->wl_shm)
           wl_shm_destroy(dri2_dpy->wl_shm);
       wl_registry_destroy(dri2_dpy->wl_registry);
       wl_event_queue_destroy(dri2_dpy->wl_queue);
       wl_proxy_wrapper_destroy(dri2_dpy->wl_dpy_wrapper);
+      u_vector_finish(&dri2_dpy->wl_modifiers.argb8888);
+      u_vector_finish(&dri2_dpy->wl_modifiers.xrgb8888);
+      u_vector_finish(&dri2_dpy->wl_modifiers.rgb565);
       if (dri2_dpy->own_device) {
          wl_display_disconnect(dri2_dpy->wl_dpy);
       }
index 5b3e93abe0f7b010cba5f7a44a94e4243491d294..0388b642cd5b6b6bcc6e7f25b3c1606d525c7db5 100644 (file)
@@ -45,6 +45,8 @@
 #ifdef HAVE_WAYLAND_PLATFORM
 #include <wayland-client.h>
 #include "wayland-egl-priv.h"
+/* forward declarations of protocol elements */
+struct zwp_linux_dmabuf_v1;
 #endif
 
 #include <GL/gl.h>
@@ -73,6 +75,8 @@
 #include "eglimage.h"
 #include "eglsync.h"
 
+#include "util/u_vector.h"
+
 struct wl_buffer;
 
 struct dri2_egl_driver
@@ -212,6 +216,12 @@ struct dri2_egl_display
    struct wl_drm            *wl_drm;
    struct wl_shm            *wl_shm;
    struct wl_event_queue    *wl_queue;
+   struct zwp_linux_dmabuf_v1 *wl_dmabuf;
+   struct {
+      struct u_vector        xrgb8888;
+      struct u_vector        argb8888;
+      struct u_vector        rgb565;
+   } wl_modifiers;
    bool                      authenticated;
    int                       formats;
    uint32_t                  capabilities;
index 4f941a16aa87c45dff16b5eff0739374cec493f5..211036f45f4b0e6e072a1efe1a2b39b5f7dee370 100644 (file)
 #include <unistd.h>
 #include <fcntl.h>
 #include <xf86drm.h>
+#include <drm_fourcc.h>
 #include <sys/mman.h>
 
 #include "egl_dri2.h"
 #include "egl_dri2_fallbacks.h"
 #include "loader.h"
+#include "util/u_vector.h"
 
 #include <wayland-client.h>
 #include "wayland-drm-client-protocol.h"
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
+
+#ifndef DRM_FORMAT_MOD_INVALID
+#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
+#endif
+
+#ifndef DRM_FORMAT_MOD_LINEAR
+#define DRM_FORMAT_MOD_LINEAR 0
+#endif
 
 enum wl_drm_format_flags {
    HAS_ARGB8888 = 1,
@@ -331,6 +342,8 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
       dri2_egl_display(dri2_surf->base.Resource.Display);
    int use_flags;
    unsigned int dri_image_format;
+   uint64_t *modifiers;
+   int num_modifiers;
 
    /* currently supports three WL DRM formats,
     * WL_DRM_FORMAT_ARGB8888, WL_DRM_FORMAT_XRGB8888,
@@ -339,12 +352,18 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
    switch (dri2_surf->format) {
    case WL_DRM_FORMAT_ARGB8888:
       dri_image_format = __DRI_IMAGE_FORMAT_ARGB8888;
+      modifiers = u_vector_tail(&dri2_dpy->wl_modifiers.argb8888);
+      num_modifiers = u_vector_length(&dri2_dpy->wl_modifiers.argb8888);
       break;
    case WL_DRM_FORMAT_XRGB8888:
       dri_image_format = __DRI_IMAGE_FORMAT_XRGB8888;
+      modifiers = u_vector_tail(&dri2_dpy->wl_modifiers.xrgb8888);
+      num_modifiers = u_vector_length(&dri2_dpy->wl_modifiers.xrgb8888);
       break;
    case WL_DRM_FORMAT_RGB565:
       dri_image_format = __DRI_IMAGE_FORMAT_RGB565;
+      modifiers = u_vector_tail(&dri2_dpy->wl_modifiers.rgb565);
+      num_modifiers = u_vector_length(&dri2_dpy->wl_modifiers.rgb565);
       break;
    default:
       /* format is not supported */
@@ -382,27 +401,60 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
 
    if (dri2_dpy->is_different_gpu &&
        dri2_surf->back->linear_copy == NULL) {
-       dri2_surf->back->linear_copy =
-          dri2_dpy->image->createImage(dri2_dpy->dri_screen,
-                                      dri2_surf->base.Width,
-                                      dri2_surf->base.Height,
-                                      dri_image_format,
-                                      use_flags |
-                                      __DRI_IMAGE_USE_LINEAR,
-                                      NULL);
+      /* The LINEAR modifier should be a perfect alias of the LINEAR use
+       * flag; try the new interface first before the old, then fall back. */
+      if (dri2_dpy->image->base.version >= 15 &&
+           dri2_dpy->image->createImageWithModifiers) {
+         uint64_t linear_mod = DRM_FORMAT_MOD_LINEAR;
+
+         dri2_surf->back->linear_copy =
+            dri2_dpy->image->createImageWithModifiers(dri2_dpy->dri_screen,
+                                                      dri2_surf->base.Width,
+                                                      dri2_surf->base.Height,
+                                                      dri_image_format,
+                                                      &linear_mod,
+                                                      1,
+                                                      NULL);
+      } else {
+         dri2_surf->back->linear_copy =
+            dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+                                         dri2_surf->base.Width,
+                                         dri2_surf->base.Height,
+                                         dri_image_format,
+                                         use_flags |
+                                         __DRI_IMAGE_USE_LINEAR,
+                                         NULL);
+      }
       if (dri2_surf->back->linear_copy == NULL)
           return -1;
    }
 
    if (dri2_surf->back->dri_image == NULL) {
-      dri2_surf->back->dri_image =
-         dri2_dpy->image->createImage(dri2_dpy->dri_screen,
-                                      dri2_surf->base.Width,
-                                      dri2_surf->base.Height,
-                                      dri_image_format,
-                                      dri2_dpy->is_different_gpu ?
-                                         0 : use_flags,
-                                      NULL);
+      /* If our DRIImage implementation does not support
+       * createImageWithModifiers, then fall back to the old createImage,
+       * and hope it allocates an image which is acceptable to the winsys.
+        */
+      if (num_modifiers && dri2_dpy->image->base.version >= 15 &&
+          dri2_dpy->image->createImageWithModifiers) {
+         dri2_surf->back->dri_image =
+           dri2_dpy->image->createImageWithModifiers(dri2_dpy->dri_screen,
+                                                     dri2_surf->base.Width,
+                                                     dri2_surf->base.Height,
+                                                     dri_image_format,
+                                                     modifiers,
+                                                     num_modifiers,
+                                                     NULL);
+      } else {
+         dri2_surf->back->dri_image =
+            dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+                                         dri2_surf->base.Width,
+                                         dri2_surf->base.Height,
+                                         dri_image_format,
+                                         dri2_dpy->is_different_gpu ?
+                                              0 : use_flags,
+                                         NULL);
+      }
+
       dri2_surf->back->age = 0;
    }
    if (dri2_surf->back->dri_image == NULL)
@@ -643,17 +695,68 @@ create_wl_buffer(struct dri2_egl_display *dri2_dpy,
                  __DRIimage *image)
 {
    struct wl_buffer *ret;
-   int width, height, fourcc;
+   int width, height, fourcc, num_planes;
 
    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_FOURCC, &fourcc);
+   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NUM_PLANES,
+                               &num_planes);
+
+   if (dri2_dpy->wl_dmabuf && dri2_dpy->image->base.version >= 15) {
+      struct zwp_linux_buffer_params_v1 *params;
+      int mod_hi, mod_lo;
+      int i;
+
+      dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_MODIFIER_UPPER,
+                                  &mod_hi);
+      dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_MODIFIER_LOWER,
+                                  &mod_lo);
+
+      /* We don't need a wrapper for wl_dmabuf objects, because we have to
+       * create the intermediate params object; we can set the queue on this,
+       * and the wl_buffer inherits it race-free. */
+      params = zwp_linux_dmabuf_v1_create_params(dri2_dpy->wl_dmabuf);
+      if (dri2_surf)
+         wl_proxy_set_queue((struct wl_proxy *) params, dri2_surf->wl_queue);
+
+      for (i = 0; i < num_planes; i++) {
+         __DRIimage *p_image;
+         int stride, offset, fd;
+
+         if (i == 0)
+            p_image = image;
+         else
+            p_image = dri2_dpy->image->fromPlanar(image, i, NULL);
+         if (!p_image) {
+            zwp_linux_buffer_params_v1_destroy(params);
+            return NULL;
+         }
 
-   if (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) {
+         dri2_dpy->image->queryImage(p_image, __DRI_IMAGE_ATTRIB_FD, &fd);
+         dri2_dpy->image->queryImage(p_image, __DRI_IMAGE_ATTRIB_STRIDE,
+                                     &stride);
+         dri2_dpy->image->queryImage(p_image, __DRI_IMAGE_ATTRIB_OFFSET,
+                                     &offset);
+         if (image != p_image)
+            dri2_dpy->image->destroyImage(p_image);
+
+         zwp_linux_buffer_params_v1_add(params, fd, i, offset, stride,
+                                        mod_hi, mod_lo);
+         close(fd);
+      }
+
+      ret = zwp_linux_buffer_params_v1_create_immed(params, width, height,
+                                                    fourcc, 0);
+      zwp_linux_buffer_params_v1_destroy(params);
+   } else if (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) {
       struct wl_drm *wl_drm =
          dri2_surf ? dri2_surf->wl_drm_wrapper : dri2_dpy->wl_drm;
       int fd, stride;
 
+      if (num_planes > 1)
+         return NULL;
+
       dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD, &fd);
       dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &stride);
       ret = wl_drm_create_prime_buffer(wl_drm, fd, width, height, fourcc, 0,
@@ -664,6 +767,9 @@ create_wl_buffer(struct dri2_egl_display *dri2_dpy,
          dri2_surf ? dri2_surf->wl_drm_wrapper : dri2_dpy->wl_drm;
       int name, stride;
 
+      if (num_planes > 1)
+         return NULL;
+
       dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NAME, &name);
       dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &stride);
       ret = wl_drm_create_buffer(wl_drm, name, width, height, stride, fourcc);
@@ -948,6 +1054,47 @@ static const struct wl_drm_listener drm_listener = {
    .capabilities = drm_handle_capabilities
 };
 
+static void
+dmabuf_ignore_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
+                     uint32_t format)
+{
+   /* formats are implicitly advertised by the 'modifier' event, so ignore */
+}
+
+static void
+dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
+                       uint32_t format, uint32_t modifier_hi,
+                       uint32_t modifier_lo)
+{
+   struct dri2_egl_display *dri2_dpy = data;
+   uint64_t *mod = NULL;
+
+   switch (format) {
+   case WL_DRM_FORMAT_ARGB8888:
+      mod = u_vector_add(&dri2_dpy->wl_modifiers.argb8888);
+      break;
+   case WL_DRM_FORMAT_XRGB8888:
+      mod = u_vector_add(&dri2_dpy->wl_modifiers.xrgb8888);
+      break;
+   case WL_DRM_FORMAT_RGB565:
+      mod = u_vector_add(&dri2_dpy->wl_modifiers.rgb565);
+      break;
+   default:
+      break;
+   }
+
+   if (!mod)
+      return;
+
+   *mod = (uint64_t) modifier_hi << 32;
+   *mod |= (uint64_t) (modifier_lo & 0xffffffff);
+}
+
+static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
+   .format = dmabuf_ignore_format,
+   .modifier = dmabuf_handle_modifier,
+};
+
 static void
 registry_handle_global_drm(void *data, struct wl_registry *registry,
                            uint32_t name, const char *interface,
@@ -959,6 +1106,12 @@ registry_handle_global_drm(void *data, struct wl_registry *registry,
       dri2_dpy->wl_drm =
          wl_registry_bind(registry, name, &wl_drm_interface, MIN2(version, 2));
       wl_drm_add_listener(dri2_dpy->wl_drm, &drm_listener, dri2_dpy);
+   } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0 && version >= 3) {
+      dri2_dpy->wl_dmabuf =
+         wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface,
+                          MIN2(version, 3));
+      zwp_linux_dmabuf_v1_add_listener(dri2_dpy->wl_dmabuf, &dmabuf_listener,
+                                       dri2_dpy);
    }
 }
 
@@ -1127,6 +1280,12 @@ dri2_initialize_wayland_drm(_EGLDriver *drv, _EGLDisplay *disp)
       dri2_dpy->wl_dpy = disp->PlatformDisplay;
    }
 
+   if (!u_vector_init(&dri2_dpy->wl_modifiers.xrgb8888, sizeof(uint64_t), 32) ||
+       !u_vector_init(&dri2_dpy->wl_modifiers.argb8888, sizeof(uint64_t), 32) ||
+       !u_vector_init(&dri2_dpy->wl_modifiers.rgb565, sizeof(uint64_t), 32)) {
+      goto cleanup;
+   }
+
    dri2_dpy->wl_queue = wl_display_create_queue(dri2_dpy->wl_dpy);
 
    dri2_dpy->wl_dpy_wrapper = wl_proxy_create_wrapper(dri2_dpy->wl_dpy);