diff --git a/configure.ac b/configure.ac index 92ae00e5e..11af8623f 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 6c054e9ed..10c288db3 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 b618d97de..ec5a0259b 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 0a648a6c4..87263453d 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 1b10c51ff..63d5380be 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 #include #include +#include + +#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 39ec8fe99..5345033c2 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 000000000..93bf32beb --- /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 + * Daniel Stone + */ + +#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 + +#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 000000000..28f14b4c2 --- /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 + * Daniel Stone + */ + +#ifndef META_WAYLAND_DMA_BUF_H +#define META_WAYLAND_DMA_BUF_H + +#include +#include + +#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 044cc5e5a..cbd794a04 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 383c5dafd..b5ab1ecf6 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");