From 02cc359372773800de817950aebdf9be2c7973d1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 16 Jun 2017 18:01:23 +0100 Subject: [PATCH] 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 --- configure.ac | 5 +- src/egl/Makefile.am | 23 ++- src/egl/drivers/dri2/.gitignore | 2 + src/egl/drivers/dri2/egl_dri2.c | 7 + src/egl/drivers/dri2/egl_dri2.h | 10 ++ src/egl/drivers/dri2/platform_wayland.c | 195 +++++++++++++++++++++--- 6 files changed, 221 insertions(+), 21 deletions(-) create mode 100644 src/egl/drivers/dri2/.gitignore diff --git a/configure.ac b/configure.ac index 46fcd8f3fe7..c5803e0f6ec 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/src/egl/Makefile.am b/src/egl/Makefile.am index 81090387b58..19295de3ed6 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 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 index 00000000000..e96becbb542 --- /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 072494ed4ed..a197e0456fd 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 @@ 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); } diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index 5b3e93abe0f..0388b642cd5 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 #include "wayland-egl-priv.h" +/* forward declarations of protocol elements */ +struct zwp_linux_dmabuf_v1; #endif #include @@ -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; diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c index 4f941a16aa8..211036f45f4 100644 --- a/src/egl/drivers/dri2/platform_wayland.c +++ b/src/egl/drivers/dri2/platform_wayland.c @@ -36,14 +36,25 @@ #include #include #include +#include #include #include "egl_dri2.h" #include "egl_dri2_fallbacks.h" #include "loader.h" +#include "util/u_vector.h" #include #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->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 @@ 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);