egl/wayland: Use linux-dmabuf interface for buffers
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>
diff --git a/src/egl/Makefile.am b/src/egl/Makefile.am
index 8109038..19295de 100644
--- a/src/egl/Makefile.am
+++ b/src/egl/Makefile.am
@@ -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
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 @@
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 @@
$(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
index 0000000..e96becb
--- /dev/null
+++ b/src/egl/drivers/dri2/.gitignore
@@ -0,0 +1,2 @@
+linux-dmabuf-unstable-v1-client-protocol.h
+linux-dmabuf-unstable-v1-protocol.c
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index 072494e..a197e04 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -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 @@
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);
}
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 5b3e93a..0388b64 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -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 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;
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index 4f941a1..211036f 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -36,14 +36,25 @@
#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 @@
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 @@
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 @@
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 @@
__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->capabilities & WL_DRM_CAPABILITY_PRIME) {
+ 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;
+ }
+
+ 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 @@
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);
@@ -949,6 +1055,47 @@
};
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,
uint32_t version)
@@ -959,6 +1106,12 @@
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_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);