[mutter] wayland: Add zwp_linux_dmabuf_v1 support



commit b7b5fb293d1de3af9e533f2063749fde7a790945
Author: Daniel Stone <daniels collabora com>
Date:   Tue Jul 11 16:03:26 2017 +0100

    wayland: Add zwp_linux_dmabuf_v1 support
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785262
    Signed-off-by: Daniel Stone <daniels collabora com>

 configure.ac                        |    2 +-
 src/Makefile.am                     |    4 +
 src/backends/meta-egl.c             |   51 +++
 src/backends/meta-egl.h             |   16 +
 src/wayland/meta-wayland-buffer.c   |   20 ++
 src/wayland/meta-wayland-buffer.h   |    6 +
 src/wayland/meta-wayland-dma-buf.c  |  585 +++++++++++++++++++++++++++++++++++
 src/wayland/meta-wayland-dma-buf.h  |   50 +++
 src/wayland/meta-wayland-versions.h |    1 +
 src/wayland/meta-wayland.c          |    2 +
 10 files changed, 736 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 92ae00e..11af862 100644
--- a/configure.ac
+++ b/configure.ac
@@ -292,7 +292,7 @@ AS_IF([test "$have_wayland" = "yes"], [
   AC_SUBST([WAYLAND_SCANNER])
   AC_DEFINE([HAVE_WAYLAND],[1],[Define if you want to enable Wayland support])
 
-  PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.7],
+  PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.9],
                    [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`])
   AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
 ])
diff --git a/src/Makefile.am b/src/Makefile.am
index 6c054e9..10c288d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -71,6 +71,8 @@ mutter_built_sources += \
        tablet-unstable-v2-server-protocol.h                            \
        xdg-foreign-unstable-v1-protocol.c                              \
        xdg-foreign-unstable-v1-server-protocol.h                       \
+       linux-dmabuf-unstable-v1-protocol.c                                     \
+       linux-dmabuf-unstable-v1-server-protocol.h                              \
        $(NULL)
 endif
 
@@ -317,6 +319,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES +=     \
        wayland/meta-xwayland-private.h         \
        wayland/meta-wayland-buffer.c           \
        wayland/meta-wayland-buffer.h           \
+       wayland/meta-wayland-dma-buf.c          \
+       wayland/meta-wayland-dma-buf.h          \
        wayland/meta-wayland-region.c           \
        wayland/meta-wayland-region.h           \
        wayland/meta-wayland-data-device.c      \
diff --git a/src/backends/meta-egl.c b/src/backends/meta-egl.c
index b618d97..ec5a025 100644
--- a/src/backends/meta-egl.c
+++ b/src/backends/meta-egl.c
@@ -66,6 +66,9 @@ struct _MetaEgl
 
   PFNEGLSTREAMCONSUMERACQUIREKHRPROC eglStreamConsumerAcquireKHR;
   PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC eglStreamConsumerAcquireAttribNV;
+
+  PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT;
+  PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT;
 };
 
 G_DEFINE_TYPE (MetaEgl, meta_egl, G_TYPE_OBJECT)
@@ -711,6 +714,51 @@ meta_egl_stream_consumer_acquire (MetaEgl     *egl,
   return TRUE;
 }
 
+gboolean
+meta_egl_query_dma_buf_formats (MetaEgl   *egl,
+                                EGLDisplay display,
+                                EGLint     max_formats,
+                                EGLint    *formats,
+                                EGLint    *num_formats,
+                                GError   **error)
+{
+  if (!is_egl_proc_valid (egl->eglQueryDmaBufFormatsEXT, error))
+    return FALSE;
+
+  if (!egl->eglQueryDmaBufFormatsEXT (display, max_formats, formats,
+                                      num_formats))
+    {
+      set_egl_error (error);
+      return FALSE;
+    }
+
+    return TRUE;
+}
+
+gboolean
+meta_egl_query_dma_buf_modifiers (MetaEgl      *egl,
+                                  EGLDisplay    display,
+                                  EGLint        format,
+                                  EGLint        max_modifiers,
+                                  EGLuint64KHR *modifiers,
+                                  EGLBoolean   *external_only,
+                                  EGLint       *num_modifiers,
+                                  GError      **error)
+{
+  if (!is_egl_proc_valid (egl->eglQueryDmaBufModifiersEXT, error))
+    return FALSE;
+
+  if (!egl->eglQueryDmaBufModifiersEXT (display, format, max_modifiers,
+                                        modifiers, external_only,
+                                        num_modifiers))
+    {
+      set_egl_error (error);
+      return FALSE;
+    }
+
+    return TRUE;
+}
+
 #define GET_EGL_PROC_ADDR(proc) \
   egl->proc = (void *) eglGetProcAddress (#proc);
 
@@ -753,6 +801,9 @@ meta_egl_constructed (GObject *object)
 
   GET_EGL_PROC_ADDR (eglStreamConsumerAcquireKHR);
   GET_EGL_PROC_ADDR (eglStreamConsumerAcquireAttribNV);
+
+  GET_EGL_PROC_ADDR (eglQueryDmaBufFormatsEXT);
+  GET_EGL_PROC_ADDR (eglQueryDmaBufModifiersEXT);
 }
 
 #undef GET_EGL_PROC_ADDR
diff --git a/src/backends/meta-egl.h b/src/backends/meta-egl.h
index 0a648a6..8726345 100644
--- a/src/backends/meta-egl.h
+++ b/src/backends/meta-egl.h
@@ -167,4 +167,20 @@ gboolean meta_egl_stream_consumer_gl_texture_external (MetaEgl     *egl,
                                                        EGLStreamKHR stream,
                                                        GError     **error);
 
+gboolean meta_egl_query_dma_buf_formats (MetaEgl    *egl,
+                                         EGLDisplay  display,
+                                         EGLint      max_formats,
+                                         EGLint     *formats,
+                                         EGLint     *num_formats,
+                                         GError    **error);
+
+gboolean meta_egl_query_dma_buf_modifiers (MetaEgl      *egl,
+                                           EGLDisplay    display,
+                                           EGLint        format,
+                                           EGLint        max_modifiers,
+                                           EGLuint64KHR *modifiers,
+                                           EGLBoolean   *external_only,
+                                           EGLint       *num_formats,
+                                           GError      **error);
+
 #endif /* META_EGL_H */
diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c
index 1b10c51..63d5380 100644
--- a/src/wayland/meta-wayland-buffer.c
+++ b/src/wayland/meta-wayland-buffer.c
@@ -25,11 +25,18 @@
 #include "config.h"
 
 #include "meta-wayland-buffer.h"
+#include "meta-wayland-dma-buf.h"
 
 #include <clutter/clutter.h>
 #include <cogl/cogl-egl.h>
 #include <meta/util.h>
 
+#include <drm_fourcc.h>
+
+#ifndef DRM_FORMAT_MOD_INVALID
+#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
+#endif
+
 #include "backends/meta-backend-private.h"
 
 enum
@@ -97,6 +104,7 @@ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
   MetaWaylandEglStream *stream;
+  MetaWaylandDmaBufBuffer *dma_buf;
 
   if (wl_shm_buffer_get (buffer->resource) != NULL)
     {
@@ -120,6 +128,14 @@ meta_wayland_buffer_realize (MetaWaylandBuffer *buffer)
       return TRUE;
     }
 
+  dma_buf = meta_wayland_dma_buf_from_buffer (buffer);
+  if (dma_buf)
+    {
+      buffer->dma_buf.dma_buf = dma_buf;
+      buffer->type = META_WAYLAND_BUFFER_TYPE_DMA_BUF;
+      return TRUE;
+    }
+
   return FALSE;
 }
 
@@ -341,6 +357,8 @@ meta_wayland_buffer_attach (MetaWaylandBuffer *buffer,
       return egl_image_buffer_attach (buffer, error);
     case META_WAYLAND_BUFFER_TYPE_EGL_STREAM:
       return egl_stream_buffer_attach (buffer, error);
+    case META_WAYLAND_BUFFER_TYPE_DMA_BUF:
+      return meta_wayland_dma_buf_buffer_attach (buffer, error);
     case META_WAYLAND_BUFFER_TYPE_UNKNOWN:
       g_assert_not_reached ();
       return FALSE;
@@ -430,6 +448,7 @@ meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer,
       res = process_shm_buffer_damage (buffer, region, &error);
     case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE:
     case META_WAYLAND_BUFFER_TYPE_EGL_STREAM:
+    case META_WAYLAND_BUFFER_TYPE_DMA_BUF:
       res = TRUE;
       break;
     case META_WAYLAND_BUFFER_TYPE_UNKNOWN:
@@ -453,6 +472,7 @@ meta_wayland_buffer_finalize (GObject *object)
 
   g_clear_pointer (&buffer->texture, cogl_object_unref);
   g_clear_object (&buffer->egl_stream.stream);
+  g_clear_object (&buffer->dma_buf.dma_buf);
 
   G_OBJECT_CLASS (meta_wayland_buffer_parent_class)->finalize (object);
 }
diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h
index 39ec8fe..5345033 100644
--- a/src/wayland/meta-wayland-buffer.h
+++ b/src/wayland/meta-wayland-buffer.h
@@ -31,6 +31,7 @@
 
 #include "meta-wayland-types.h"
 #include "meta-wayland-egl-stream.h"
+#include "meta-wayland-dma-buf.h"
 
 typedef enum _MetaWaylandBufferType
 {
@@ -38,6 +39,7 @@ typedef enum _MetaWaylandBufferType
   META_WAYLAND_BUFFER_TYPE_SHM,
   META_WAYLAND_BUFFER_TYPE_EGL_IMAGE,
   META_WAYLAND_BUFFER_TYPE_EGL_STREAM,
+  META_WAYLAND_BUFFER_TYPE_DMA_BUF,
 } MetaWaylandBufferType;
 
 struct _MetaWaylandBuffer
@@ -55,6 +57,10 @@ struct _MetaWaylandBuffer
   struct {
     MetaWaylandEglStream *stream;
   } egl_stream;
+
+  struct {
+    MetaWaylandDmaBufBuffer *dma_buf;
+  } dma_buf;
 };
 
 #define META_TYPE_WAYLAND_BUFFER (meta_wayland_buffer_get_type ())
diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c
new file mode 100644
index 0000000..93bf32b
--- /dev/null
+++ b/src/wayland/meta-wayland-dma-buf.c
@@ -0,0 +1,585 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2016 Red Hat Inc.
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ *     Daniel Stone <daniels collabora com>
+ */
+
+#include "config.h"
+
+#include "wayland/meta-wayland-dma-buf.h"
+
+#include "cogl/cogl.h"
+#include "cogl/cogl-egl.h"
+#include "backends/meta-backend-private.h"
+#include "backends/meta-egl.h"
+#include "backends/meta-egl-ext.h"
+#include "meta/meta-backend.h"
+#include "wayland/meta-wayland-buffer.h"
+#include "wayland/meta-wayland-private.h"
+#include "wayland/meta-wayland-versions.h"
+
+#include <drm_fourcc.h>
+
+#include "linux-dmabuf-unstable-v1-server-protocol.h"
+
+#ifndef DRM_FORMAT_MOD_INVALID
+#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
+#endif
+
+#define META_WAYLAND_DMA_BUF_MAX_FDS 4
+
+struct _MetaWaylandDmaBufBuffer
+{
+  GObject parent;
+
+  int width;
+  int height;
+  uint32_t drm_format;
+  uint64_t drm_modifier;
+  bool is_y_inverted;
+  int fds[META_WAYLAND_DMA_BUF_MAX_FDS];
+  int offsets[META_WAYLAND_DMA_BUF_MAX_FDS];
+  unsigned int strides[META_WAYLAND_DMA_BUF_MAX_FDS];
+};
+
+G_DEFINE_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, G_TYPE_OBJECT);
+
+gboolean
+meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer *buffer,
+                                    GError           **error)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaEgl *egl = meta_backend_get_egl (backend);
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
+  MetaWaylandDmaBufBuffer *dma_buf = buffer->dma_buf.dma_buf;
+  CoglPixelFormat cogl_format;
+  EGLImageKHR egl_image;
+  CoglTexture2D *texture;
+  EGLint attribs[64];
+  int attr_idx = 0;
+
+  if (buffer->texture)
+    return TRUE;
+
+  /* DRM_FORMAT_* enums consider the entire pixel as a single packed quantity,
+   * with little-endian ordering. COGL_PIXEL_FORMAT_* is in byte order when
+   * each channel is an 8-byte unit. Hence these have order swapped. */
+  switch (dma_buf->drm_format)
+    {
+    case DRM_FORMAT_XRGB8888:
+      cogl_format = COGL_PIXEL_FORMAT_BGR_888;
+      break;
+    case DRM_FORMAT_ARGB8888:
+      cogl_format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+      break;
+    case DRM_FORMAT_ARGB2101010:
+      cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010_PRE;
+      break;
+    case DRM_FORMAT_RGB565:
+      cogl_format = COGL_PIXEL_FORMAT_RGB_565;
+      break;
+    default:
+      g_set_error (error, G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "Unsupported buffer format %d", dma_buf->drm_format);
+      return FALSE;
+    }
+
+  attribs[attr_idx++] = EGL_WIDTH;
+  attribs[attr_idx++] = dma_buf->width;
+  attribs[attr_idx++] = EGL_HEIGHT;
+  attribs[attr_idx++] = dma_buf->height;
+  attribs[attr_idx++] = EGL_LINUX_DRM_FOURCC_EXT;
+  attribs[attr_idx++] = dma_buf->drm_format;
+
+  attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_FD_EXT;
+  attribs[attr_idx++] = dma_buf->fds[0];
+  attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
+  attribs[attr_idx++] = dma_buf->offsets[0];
+  attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
+  attribs[attr_idx++] = dma_buf->strides[0];
+  attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
+  attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+  attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
+  attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+
+  if (dma_buf->fds[1] >= 0)
+    {
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_FD_EXT;
+      attribs[attr_idx++] = dma_buf->fds[1];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
+      attribs[attr_idx++] = dma_buf->offsets[1];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
+      attribs[attr_idx++] = dma_buf->strides[1];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
+      attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
+      attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+    }
+
+  if (dma_buf->fds[2] >= 0)
+    {
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_FD_EXT;
+      attribs[attr_idx++] = dma_buf->fds[2];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
+      attribs[attr_idx++] = dma_buf->offsets[2];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
+      attribs[attr_idx++] = dma_buf->strides[2];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
+      attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
+      attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+    }
+
+  if (dma_buf->fds[3] >= 0)
+    {
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_FD_EXT;
+      attribs[attr_idx++] = dma_buf->fds[3];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
+      attribs[attr_idx++] = dma_buf->offsets[3];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
+      attribs[attr_idx++] = dma_buf->strides[3];
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
+      attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff;
+      attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
+      attribs[attr_idx++] = dma_buf->drm_modifier >> 32;
+    }
+
+  attribs[attr_idx++] = EGL_NONE;
+  attribs[attr_idx++] = EGL_NONE;
+
+  /* The EXT_image_dma_buf_import spec states that EGL_NO_CONTEXT is to be used
+   * in conjunction with the EGL_LINUX_DMA_BUF_EXT target. Similarly, the
+   * native buffer is named in the attribs. */
+  egl_image = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT,
+                                     EGL_LINUX_DMA_BUF_EXT, NULL, attribs,
+                                     error);
+  if (egl_image == EGL_NO_IMAGE_KHR)
+    return FALSE;
+
+  texture = cogl_egl_texture_2d_new_from_image (cogl_context,
+                                                dma_buf->width,
+                                                dma_buf->height,
+                                                cogl_format,
+                                                egl_image,
+                                                error);
+
+  meta_egl_destroy_image (egl, egl_display, egl_image, NULL);
+
+  if (!texture)
+    return FALSE;
+
+  buffer->texture = COGL_TEXTURE (texture);
+  buffer->is_y_inverted = dma_buf->is_y_inverted;
+
+  return TRUE;
+}
+
+static void
+buffer_params_add (struct wl_client   *client,
+                   struct wl_resource *resource,
+                   int32_t             fd,
+                   uint32_t            plane_idx,
+                   uint32_t            offset,
+                   uint32_t            stride,
+                   uint32_t            drm_modifier_hi,
+                   uint32_t            drm_modifier_lo)
+{
+  MetaWaylandDmaBufBuffer *dma_buf;
+  uint64_t drm_modifier;
+
+  drm_modifier = ((uint64_t) drm_modifier_hi) << 32;
+  drm_modifier |= ((uint64_t) drm_modifier_lo) & 0xffffffff;
+
+  dma_buf = wl_resource_get_user_data (resource);
+  if (!dma_buf)
+    {
+      wl_resource_post_error (resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+                              "params already used");
+      return;
+    }
+
+  if (plane_idx >= META_WAYLAND_DMA_BUF_MAX_FDS)
+    {
+      wl_resource_post_error (resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
+                              "out-of-bounds plane index %d",
+                              plane_idx);
+      return;
+    }
+
+  if (dma_buf->fds[plane_idx] != -1)
+    {
+      wl_resource_post_error (resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
+                              "plane index %d already set",
+                              plane_idx);
+      return;
+    }
+
+  if (dma_buf->drm_modifier != DRM_FORMAT_MOD_INVALID &&
+      dma_buf->drm_modifier != drm_modifier)
+    {
+      wl_resource_post_error (resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
+                              "mismatching modifier between planes");
+      return;
+    }
+
+  dma_buf->drm_modifier = drm_modifier;
+  dma_buf->fds[plane_idx] = fd;
+  dma_buf->offsets[plane_idx] = offset;
+  dma_buf->strides[plane_idx] = stride;
+}
+
+static void
+buffer_params_destroy (struct wl_client   *client,
+                       struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+buffer_params_destructor (struct wl_resource *resource)
+{
+  MetaWaylandDmaBufBuffer *dma_buf;
+
+  /* The user-data for our MetaWaylandBuffer is only valid in between adding
+   * FDs and creating the buffer; once it is created, we free it out into
+   * the wild, where the ref is considered transferred to the wl_buffer. */
+  dma_buf = wl_resource_get_user_data (resource);
+  if (dma_buf)
+    g_object_unref (dma_buf);
+}
+
+static void
+buffer_destroy (struct wl_client   *client,
+                struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static const struct wl_buffer_interface dma_buf_buffer_impl =
+{
+  buffer_destroy,
+};
+
+MetaWaylandDmaBufBuffer *
+meta_wayland_dma_buf_from_buffer (MetaWaylandBuffer *buffer)
+{
+  if (wl_resource_instance_of (buffer->resource, &wl_buffer_interface,
+                               &dma_buf_buffer_impl))
+      return wl_resource_get_user_data (buffer->resource);
+
+  return NULL;
+}
+
+static void
+buffer_params_create_common (struct wl_client   *client,
+                             struct wl_resource *params_resource,
+                             uint32_t            buffer_id,
+                             int32_t             width,
+                             int32_t             height,
+                             uint32_t            drm_format,
+                             uint32_t            flags)
+{
+  MetaWaylandDmaBufBuffer *dma_buf;
+  MetaWaylandBuffer *buffer;
+  struct wl_resource *buffer_resource;
+  GError *error = NULL;
+
+  dma_buf = wl_resource_get_user_data (params_resource);
+  if (!dma_buf)
+    {
+      wl_resource_post_error (params_resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+                              "params already used");
+      return;
+    }
+
+  /* Calling the 'create' method is the point of no return: after that point,
+   * the params object cannot be used. This method must either transfer the
+   * ownership of the MetaWaylandDmaBufBuffer to a MetaWaylandBuffer, or
+   * destroy it. */
+  wl_resource_set_user_data (params_resource, NULL);
+
+  if (dma_buf->fds[0] == -1)
+    {
+      wl_resource_post_error (params_resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+                              "no planes added to params");
+      g_object_unref (dma_buf);
+      return;
+    }
+
+  if ((dma_buf->fds[3] >= 0 || dma_buf->fds[2] >= 0) &&
+      (dma_buf->fds[2] == -1 || dma_buf->fds[1] == -1))
+    {
+      wl_resource_post_error (params_resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+                              "gap in planes added to params");
+      g_object_unref (dma_buf);
+      return;
+    }
+
+  dma_buf->width = width;
+  dma_buf->height = height;
+  dma_buf->drm_format = drm_format;
+  dma_buf->is_y_inverted = !(flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT);
+
+  if (flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT)
+    {
+      wl_resource_post_error (params_resource,
+                              ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
+                              "unknown flags 0x%x supplied", flags);
+      g_object_unref (dma_buf);
+      return;
+    }
+
+  /* Create a new MetaWaylandBuffer wrapping our dmabuf, and immediately try
+   * to realize it, so we can give the client success/fail feedback for the
+   * import. */
+  buffer_resource =
+    wl_resource_create (client, &wl_buffer_interface, 1, buffer_id);
+  wl_resource_set_implementation (buffer_resource, &dma_buf_buffer_impl,
+                                  dma_buf, NULL);
+  buffer = meta_wayland_buffer_from_resource (buffer_resource);
+
+  if (!meta_wayland_buffer_attach (buffer, &error))
+    {
+      if (buffer_id == 0)
+        {
+          zwp_linux_buffer_params_v1_send_failed (params_resource);
+        }
+      else
+        {
+          wl_resource_post_error (params_resource,
+                                  ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
+                                  "failed to import supplied dmabufs: %s",
+                                  error ? error->message : "unknown error");
+        }
+
+        /* will unref the MetaWaylandBuffer */
+        wl_resource_destroy (buffer->resource);
+        return;
+    }
+
+    /* If buffer_id is 0, we are using the non-immediate interface, so
+     * need to send a success event with our buffer. */
+    if (buffer_id == 0)
+      zwp_linux_buffer_params_v1_send_created (params_resource,
+                                               buffer->resource);
+}
+
+static void
+buffer_params_create (struct wl_client   *client,
+                      struct wl_resource *params_resource,
+                      int32_t             width,
+                      int32_t             height,
+                      uint32_t            format,
+                      uint32_t            flags)
+{
+  buffer_params_create_common (client, params_resource, 0, width, height,
+                               format, flags);
+}
+
+static void
+buffer_params_create_immed (struct wl_client   *client,
+                            struct wl_resource *params_resource,
+                            uint32_t            buffer_id,
+                            int32_t             width,
+                            int32_t             height,
+                            uint32_t            format,
+                            uint32_t            flags)
+{
+  buffer_params_create_common (client, params_resource, buffer_id, width,
+                               height, format, flags);
+}
+
+static const struct zwp_linux_buffer_params_v1_interface buffer_params_implementation =
+{
+  buffer_params_destroy,
+  buffer_params_add,
+  buffer_params_create,
+  buffer_params_create_immed,
+};
+
+static void
+dma_buf_handle_destroy (struct wl_client   *client,
+                        struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+dma_buf_handle_create_buffer_params (struct wl_client   *client,
+                                     struct wl_resource *dma_buf_resource,
+                                     uint32_t            params_id)
+{
+  struct wl_resource *params_resource;
+  MetaWaylandDmaBufBuffer *dma_buf;
+
+  dma_buf = g_object_new (META_TYPE_WAYLAND_DMA_BUF_BUFFER, NULL);
+
+  params_resource =
+    wl_resource_create (client,
+                        &zwp_linux_buffer_params_v1_interface,
+                        wl_resource_get_version (dma_buf_resource),
+                        params_id);
+  wl_resource_set_implementation (params_resource,
+                                  &buffer_params_implementation,
+                                  dma_buf,
+                                  buffer_params_destructor);
+}
+
+static const struct zwp_linux_dmabuf_v1_interface dma_buf_implementation =
+{
+  dma_buf_handle_destroy,
+  dma_buf_handle_create_buffer_params,
+};
+
+static void
+send_modifiers (struct wl_resource *resource,
+                uint32_t            format)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaEgl *egl = meta_backend_get_egl (backend);
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
+  EGLint num_modifiers;
+  EGLuint64KHR *modifiers;
+  GError *error = NULL;
+  gboolean ret;
+  int i;
+
+  /* First query the number of available modifiers, then allocate an array,
+   * then fill the array. */
+  ret = meta_egl_query_dma_buf_modifiers (egl, egl_display, format, 0, NULL,
+                                          NULL, &num_modifiers, NULL);
+  if (!ret || num_modifiers == 0)
+     return;
+
+  modifiers = g_new0 (uint64_t, num_modifiers);
+  ret = meta_egl_query_dma_buf_modifiers (egl, egl_display, format,
+                                          num_modifiers, modifiers, NULL,
+                                          &num_modifiers, &error);
+  if (!ret)
+    {
+      g_warning ("Failed to query modifiers for format 0x%" PRIu32 ": %s",
+                 format, error ? error->message : "unknown error");
+      g_free (modifiers);
+      return;
+    }
+
+  for (i = 0; i < num_modifiers; i++)
+    {
+      zwp_linux_dmabuf_v1_send_modifier (resource, format,
+                                         modifiers[i] >> 32,
+                                         modifiers[i] & 0xffffffff);
+    }
+
+  g_free (modifiers);
+}
+
+static void
+dma_buf_bind (struct wl_client *client,
+              void             *data,
+              uint32_t          version,
+              uint32_t          id)
+{
+  MetaWaylandCompositor *compositor = data;
+  struct wl_resource *resource;
+
+  resource = wl_resource_create (client, &zwp_linux_dmabuf_v1_interface,
+                                 version, id);
+  wl_resource_set_implementation (resource, &dma_buf_implementation,
+                                  compositor, NULL);
+  send_modifiers (resource, DRM_FORMAT_ARGB8888);
+  send_modifiers (resource, DRM_FORMAT_XRGB8888);
+  send_modifiers (resource, DRM_FORMAT_ARGB2101010);
+  send_modifiers (resource, DRM_FORMAT_RGB565);
+}
+
+gboolean
+meta_wayland_dma_buf_init (MetaWaylandCompositor *compositor)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaEgl *egl = meta_backend_get_egl (backend);
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
+
+  g_assert (backend && egl && clutter_backend && cogl_context && egl_display);
+
+  if (!meta_egl_has_extensions (egl, egl_display, NULL,
+                                "EGL_EXT_image_dma_buf_import_modifiers",
+                                NULL))
+    return FALSE;
+
+  if (!wl_global_create (compositor->wayland_display,
+                         &zwp_linux_dmabuf_v1_interface,
+                         META_ZWP_LINUX_DMABUF_V1_VERSION,
+                         compositor,
+                         dma_buf_bind))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+meta_wayland_dma_buf_buffer_finalize (GObject *object)
+{
+  MetaWaylandDmaBufBuffer *dma_buf = META_WAYLAND_DMA_BUF_BUFFER (object);
+  int i;
+
+  for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++)
+    {
+      if (dma_buf->fds[i] != -1)
+        close (dma_buf->fds[i]);
+    }
+
+  G_OBJECT_CLASS (meta_wayland_dma_buf_buffer_parent_class)->finalize (object);
+}
+
+static void
+meta_wayland_dma_buf_buffer_init (MetaWaylandDmaBufBuffer *dma_buf)
+{
+  int i;
+
+  dma_buf->drm_modifier = DRM_FORMAT_MOD_INVALID;
+
+  for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++)
+    dma_buf->fds[i] = -1;
+}
+
+static void
+meta_wayland_dma_buf_buffer_class_init (MetaWaylandDmaBufBufferClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_wayland_dma_buf_buffer_finalize;
+}
diff --git a/src/wayland/meta-wayland-dma-buf.h b/src/wayland/meta-wayland-dma-buf.h
new file mode 100644
index 0000000..28f14b4
--- /dev/null
+++ b/src/wayland/meta-wayland-dma-buf.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2016 Red Hat Inc.
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ *     Daniel Stone <daniels collabora com>
+ */
+
+#ifndef META_WAYLAND_DMA_BUF_H
+#define META_WAYLAND_DMA_BUF_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "wayland/meta-wayland-types.h"
+
+#define META_TYPE_WAYLAND_DMA_BUF_BUFFER (meta_wayland_dma_buf_buffer_get_type ())
+G_DECLARE_FINAL_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer,
+                      META, WAYLAND_DMA_BUF_BUFFER, GObject);
+
+typedef struct _MetaWaylandDmaBufBuffer MetaWaylandDmaBufBuffer;
+
+gboolean meta_wayland_dma_buf_init (MetaWaylandCompositor *compositor);
+
+gboolean
+meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer *buffer,
+                                    GError           **error);
+
+MetaWaylandDmaBufBuffer *
+meta_wayland_dma_buf_from_buffer (MetaWaylandBuffer *buffer);
+
+#endif /* META_WAYLAND_DMA_BUF_H */
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index 044cc5e..cbd794a 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -47,5 +47,6 @@
 #define META_ZWP_POINTER_GESTURES_V1_VERSION    1
 #define META_ZXDG_EXPORTER_V1_VERSION       1
 #define META_ZXDG_IMPORTER_V1_VERSION       1
+#define META_ZWP_LINUX_DMABUF_V1_VERSION    3
 
 #endif
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 383c5da..b5ab1ec 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -41,6 +41,7 @@
 #include "meta-wayland-data-device.h"
 #include "meta-wayland-tablet-manager.h"
 #include "meta-wayland-xdg-foreign.h"
+#include "meta-wayland-dma-buf.h"
 
 static MetaWaylandCompositor _meta_wayland_compositor;
 static char *_display_name_override;
@@ -355,6 +356,7 @@ meta_wayland_init (void)
   meta_wayland_relative_pointer_init (compositor);
   meta_wayland_pointer_constraints_init (compositor);
   meta_wayland_xdg_foreign_init (compositor);
+  meta_wayland_dma_buf_init (compositor);
 
   if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display))
     g_error ("Failed to start X Wayland");


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]