xserver: Branch 'master' - 3 commits

Adam Jackson ajax at kemper.freedesktop.org
Tue Apr 24 20:57:52 UTC 2018


 configure.ac                            |   31 +
 hw/xwayland/Makefile.am                 |   25 
 hw/xwayland/meson.build                 |   21 
 hw/xwayland/xwayland-glamor-eglstream.c |  830 +++++++++++++++++++++++++++++
 hw/xwayland/xwayland-glamor-gbm.c       |  887 ++++++++++++++++++++++++++++++++
 hw/xwayland/xwayland-glamor.c           |  837 ++++--------------------------
 hw/xwayland/xwayland-present.c          |    9 
 hw/xwayland/xwayland.c                  |   61 +-
 hw/xwayland/xwayland.h                  |   99 +++
 include/meson.build                     |   12 
 include/xwayland-config.h.in            |   13 
 include/xwayland-config.h.meson.in      |   11 
 meson.build                             |   15 
 meson_options.txt                       |    2 
 14 files changed, 2102 insertions(+), 751 deletions(-)

New commits:
commit 54ac09717cd8c49259f53a4a227d903ebe8e0a32
Author: Lyude Paul <lyude at redhat.com>
Date:   Fri Apr 20 14:38:05 2018 -0400

    xwayland: Add glamor egl_backend for EGLStreams
    
    This adds initial support for displaying Xwayland applications through
    the use of EGLStreams and nvidia's custom wayland protocol by adding
    another egl_backend driver. This also adds some additional egl_backend
    hooks that are required to make things work properly.
    
    EGLStreams work a lot differently then the traditional way of handling
    buffers with wayland. Unfortunately, there are also a LOT of various
    pitfalls baked into it's design that need to be explained.
    
    This has a very large and unfortunate implication: direct rendering is,
    for the time being at least, impossible to do through EGLStreams. The
    main reason being that the EGLStream spec mandates that we lose the
    entire color buffer contents with each eglSwapBuffers(), which goes
    against X's requirement of not losing data with pixmaps.  no way to use
    an allocated EGLSurface as the storage for glamor rendering like we do
    with GBM, we have to rely on blitting each pixmap to it's respective
    EGLSurface producer each frame. In order to pull this off, we add two
    different additional egl_backend hooks that GBM opts out of
    implementing:
    
    - egl_backend.allow_commits for holding off displaying any EGLStream
      backed pixmaps until the point where it's stream is completely
      initialized and ready for use
    - egl_backend.post_damage for blitting the content of the EGLStream
      surface producer before Xwayland actually damages and commits the
      wl_surface to the screen.
    
    The other big pitfall here is that using nvidia's wayland-eglstreams
    helper library is also not possible for the most part. All of it's API
    for creating and destroying streams rely on being able to perform a
    roundtrip in order to bring each stream to completion since the wayland
    compositor must perform it's job of connecting a consumer to each
    EGLstream. Because Xwayland has to potentially handle both responding to
    the wayland compositor and it's own X clients, the situation of the
    wayland compositor being one of our X clients must be considered. If we
    perform a roundtrip with the Wayland compositor, it's possible that the
    wayland compositor might currently be connected to us as an X client and
    thus hang while both Xwayland and the wayland compositor await responses
    from eachother. To avoid this, we work directly with the wayland
    protocol and use wl_display_sync() events along with release() events to
    set up and destroy EGLStreams asynchronously alongside handling X
    clients.
    
    Additionally, since setting up EGLStreams is not an atomic operation we
    have to take into consideration the fact that an EGLStream can
    potentially be created in response to a window resize, then immediately
    deleted due to another pending window resize in the same X client's
    pending reqests before Xwayland hits the part of it's event loop where
    we read from the wayland compositor. To make this even more painful, we
    also have to take into consideration that since EGLStreams are not
    atomic that it's possible we could delete wayland resources for an
    EGLStream before the compositor even finishes using them and thus run
    into errors. So, we use quite a bit of tracking logic to keep EGLStream
    objects alive until we know the compositor isn't using them (even if
    this means the stream outlives the pixmap it backed).
    
    While the default backend for glamor remains GBM, this patch exists for
    users who have had to deal with the reprecussion of their GPU
    manufacturers ignoring the advice of upstream and the standardization of
    GBM across most major GPU manufacturers. It is not intended to be a
    final solution to the GBM debate, but merely a baindaid so our users
    don't have to suffer from the consequences of companies avoiding working
    upstream. New drivers are strongly encouraged not to use this as a
    backend, and use GBM like everyone else. We even spit this out as an
    error from Xwayland when using the eglstream backend.
    
    Signed-off-by: Lyude Paul <lyude at redhat.com>
    Acked-by: Daniel Stone <daniels at collabora.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/configure.ac b/configure.ac
index b20eda12f..3f9752572 100644
--- a/configure.ac
+++ b/configure.ac
@@ -590,6 +590,7 @@ AC_ARG_ENABLE(xvfb,    	      AS_HELP_STRING([--enable-xvfb], [Build Xvfb server
 AC_ARG_ENABLE(xnest,   	      AS_HELP_STRING([--enable-xnest], [Build Xnest server (default: auto)]), [XNEST=$enableval], [XNEST=auto])
 AC_ARG_ENABLE(xquartz,        AS_HELP_STRING([--enable-xquartz], [Build Xquartz server for OS-X (default: auto)]), [XQUARTZ=$enableval], [XQUARTZ=auto])
 AC_ARG_ENABLE(xwayland,       AS_HELP_STRING([--enable-xwayland], [Build Xwayland server (default: auto)]), [XWAYLAND=$enableval], [XWAYLAND=auto])
+AC_ARG_ENABLE(xwayland-eglstream, AS_HELP_STRING([--enable-xwayland-eglstream], [Build Xwayland eglstream support (default: no)]), [XWAYLAND_EGLSTREAM=$enableval], [XWAYLAND_EGLSTREAM=no])
 AC_ARG_ENABLE(standalone-xpbproxy, AS_HELP_STRING([--enable-standalone-xpbproxy], [Build a standalone xpbproxy (in addition to the one integrated into Xquartz as a separate thread) (default: no)]), [STANDALONE_XPBPROXY=$enableval], [STANDALONE_XPBPROXY=no])
 AC_ARG_ENABLE(xwin,    	      AS_HELP_STRING([--enable-xwin], [Build XWin server (default: auto)]), [XWIN=$enableval], [XWIN=auto])
 AC_ARG_ENABLE(glamor,         AS_HELP_STRING([--enable-glamor], [Build glamor dix module (default: auto)]), [GLAMOR=$enableval], [GLAMOR=auto])
@@ -2385,6 +2386,28 @@ if test "x$XWAYLAND" = xyes; then
 			  [Build xwayland with glamor support])
 	fi
 
+	PKG_CHECK_MODULES(WAYLAND_EGLSTREAM, [wayland-eglstream-protocols >= 1.0.2], [have_wl_eglstream=yes], [have_wl_eglstream=no])
+
+	if test "x$XWAYLAND_EGLSTREAM" = xauto; then
+		if test "x$have_wl_eglstream" = xyes && test "x$GLAMOR" = xyes; then
+			XWAYLAND_EGLSTREAM=yes
+		fi
+	fi
+
+	if test "x$XWAYLAND_EGLSTREAM" = xyes; then
+		if test "x$GLAMOR" != xyes; then
+			AC_MSG_ERROR([Xwayland eglstream support explicitly requested, but required modules not found.])
+		fi
+
+		if test "x$have_wl_eglstream" = xno; then
+			AC_MSG_ERROR([Xwayland eglstream support requires wayland-eglstream-protocols >= 1.0.2])
+		fi
+
+		AC_SUBST(WAYLAND_EGLSTREAM_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-eglstream-protocols`)
+		AC_DEFINE(XWL_HAS_EGLSTREAM, 1,
+			  [Build xwayland with eglstream support])
+	fi
+
 	XWAYLAND_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB $DIX_LIB $OS_LIB"
 	XWAYLAND_SYS_LIBS="$XWAYLANDMODULES_LIBS $GLX_SYS_LIBS"
 	AC_SUBST([XWAYLAND_LIBS])
@@ -2406,6 +2429,7 @@ if test "x$XWAYLAND" = xyes; then
 
 	AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`)
 fi
+AM_CONDITIONAL(XWAYLAND_EGLSTREAM, [test "x$XWAYLAND_EGLSTREAM" = "xyes"])
 
 
 dnl and the rest of these are generic, so they're in config.h
diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am
index 3fd980d0e..bc1cb8506 100644
--- a/hw/xwayland/Makefile.am
+++ b/hw/xwayland/Makefile.am
@@ -42,6 +42,11 @@ Xwayland_SOURCES += 				\
 	xwayland-glamor-xv.c
 endif
 
+if XWAYLAND_EGLSTREAM
+Xwayland_SOURCES +=				\
+	xwayland-glamor-eglstream.c
+endif
+
 glamor_built_sources =				\
 	drm-client-protocol.h			\
 	drm-protocol.c
@@ -68,12 +73,19 @@ Xwayland_built_sources +=					\
 	linux-dmabuf-unstable-v1-client-protocol.h		\
 	linux-dmabuf-unstable-v1-protocol.c
 
+if XWAYLAND_EGLSTREAM
+Xwayland_built_sources +=					\
+	wayland-eglstream-client-protocol.h			\
+	wayland-eglstream-protocol.c				\
+	wayland-eglstream-controller-client-protocol.h		\
+	wayland-eglstream-controller-protocol.c
+endif
+
 nodist_Xwayland_SOURCES = $(Xwayland_built_sources)
 CLEANFILES = $(Xwayland_built_sources)
 
 EXTRA_DIST = drm.xml
 
-
 $(Xwayland_SOURCES): $(Xwayland_built_sources)
 
 relink:
@@ -108,6 +120,16 @@ linux-dmabuf-unstable-v1-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linu
 linux-dmabuf-unstable-v1-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
 	$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
 
+wayland-eglstream-client-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
+wayland-eglstream-controller-client-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream-controller.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
+
+wayland-eglstream-protocol.c : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
+wayland-eglstream-controller-protocol.c : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream-controller.xml
+	$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
+
 %-protocol.c : %.xml
 	$(AM_V_GEN)$(WAYLAND_SCANNER) @SCANNER_ARG@ < $< > $@
 
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index ef4379aab..36bf2133a 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -51,12 +51,25 @@ srcs += code.process(xdg_output_xml)
 srcs += code.process(dmabuf_xml)
 
 xwayland_glamor = []
-if gbm_dep.found()
-    srcs += [
-        'xwayland-glamor.c',
-        'xwayland-glamor-gbm.c',
-        'xwayland-present.c',
-    ]
+eglstream_srcs = []
+if build_glamor
+    srcs += 'xwayland-glamor.c'
+    if gbm_dep.found()
+        srcs += 'xwayland-glamor-gbm.c'
+    endif
+    if build_eglstream
+        eglstream_protodir = eglstream_dep.get_pkgconfig_variable('pkgdatadir')
+        eglstream_xml = join_paths(eglstream_protodir, 'wayland-eglstream.xml')
+        eglstream_controller_xml = join_paths(eglstream_protodir, 'wayland-eglstream-controller.xml')
+
+        srcs += client_header.process(eglstream_xml)
+        srcs += client_header.process(eglstream_controller_xml)
+        srcs += code.process(eglstream_xml)
+        srcs += code.process(eglstream_controller_xml)
+
+        srcs += 'xwayland-glamor-eglstream.c'
+    endif
+    srcs += 'xwayland-present.c'
     if build_xv
         srcs += 'xwayland-glamor-xv.c'
     endif
diff --git a/hw/xwayland/xwayland-glamor-eglstream.c b/hw/xwayland/xwayland-glamor-eglstream.c
new file mode 100644
index 000000000..8dd1cc304
--- /dev/null
+++ b/hw/xwayland/xwayland-glamor-eglstream.c
@@ -0,0 +1,830 @@
+/*
+ * Copyright © 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including
+ * the next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Lyude Paul <lyude at redhat.com>
+ *
+ */
+
+#include "xwayland.h"
+
+#include "wayland-eglstream-client-protocol.h"
+#include "wayland-eglstream-controller-client-protocol.h"
+
+#define MESA_EGL_NO_X11_HEADERS
+#include <glamor_egl.h>
+#include <glamor.h>
+#include <glamor_transform.h>
+#include <glamor_transfer.h>
+
+#include <xf86drm.h>
+
+#include <epoxy/egl.h>
+
+struct xwl_eglstream_pending_stream {
+    PixmapPtr pixmap;
+    WindowPtr window;
+
+    struct xwl_pixmap *xwl_pixmap;
+    struct wl_callback *cb;
+
+    Bool is_valid;
+
+    struct xorg_list link;
+};
+
+struct xwl_eglstream_private {
+    EGLDeviceEXT egl_device;
+    struct wl_eglstream_display *display;
+    struct wl_eglstream_controller *controller;
+    uint32_t display_caps;
+
+    EGLConfig config;
+
+    SetWindowPixmapProcPtr SetWindowPixmap;
+
+    struct xorg_list pending_streams;
+
+    Bool have_egl_damage;
+
+    GLint blit_prog;
+    GLuint blit_vao;
+    GLuint blit_vbo;
+    GLuint blit_is_rgba_pos;
+};
+
+struct xwl_pixmap {
+    struct wl_buffer *buffer;
+    struct xwl_screen *xwl_screen;
+
+    /* The stream and associated resources have their own lifetime seperate
+     * from the pixmap's */
+    int refcount;
+
+    EGLStreamKHR stream;
+    EGLSurface surface;
+};
+
+static DevPrivateKeyRec xwl_eglstream_private_key;
+static DevPrivateKeyRec xwl_eglstream_window_private_key;
+
+static inline struct xwl_eglstream_private *
+xwl_eglstream_get(struct xwl_screen *xwl_screen)
+{
+    return dixLookupPrivate(&xwl_screen->screen->devPrivates,
+                            &xwl_eglstream_private_key);
+}
+
+static inline struct xwl_eglstream_pending_stream *
+xwl_eglstream_window_get_pending(WindowPtr window)
+{
+    return dixLookupPrivate(&window->devPrivates,
+                            &xwl_eglstream_window_private_key);
+}
+
+static inline void
+xwl_eglstream_window_set_pending(WindowPtr window,
+                                 struct xwl_eglstream_pending_stream *stream)
+{
+    dixSetPrivate(&window->devPrivates,
+                  &xwl_eglstream_window_private_key, stream);
+}
+
+static GLint
+xwl_eglstream_compile_glsl_prog(GLenum type, const char *source)
+{
+    GLint ok;
+    GLint prog;
+
+    prog = glCreateShader(type);
+    glShaderSource(prog, 1, (const GLchar **) &source, NULL);
+    glCompileShader(prog);
+    glGetShaderiv(prog, GL_COMPILE_STATUS, &ok);
+    if (!ok) {
+        GLchar *info;
+        GLint size;
+
+        glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size);
+        info = malloc(size);
+        if (info) {
+            glGetShaderInfoLog(prog, size, NULL, info);
+            ErrorF("Failed to compile %s: %s\n",
+                   type == GL_FRAGMENT_SHADER ? "FS" : "VS", info);
+            ErrorF("Program source:\n%s", source);
+            free(info);
+        }
+        else
+            ErrorF("Failed to get shader compilation info.\n");
+        FatalError("GLSL compile failure\n");
+    }
+
+    return prog;
+}
+
+static GLuint
+xwl_eglstream_build_glsl_prog(GLuint vs, GLuint fs)
+{
+    GLint ok;
+    GLuint prog;
+
+    prog = glCreateProgram();
+    glAttachShader(prog, vs);
+    glAttachShader(prog, fs);
+
+    glLinkProgram(prog);
+    glGetProgramiv(prog, GL_LINK_STATUS, &ok);
+    if (!ok) {
+        GLchar *info;
+        GLint size;
+
+        glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
+        info = malloc(size);
+
+        glGetProgramInfoLog(prog, size, NULL, info);
+        ErrorF("Failed to link: %s\n", info);
+        FatalError("GLSL link failure\n");
+    }
+
+    return prog;
+}
+
+static void
+xwl_eglstream_cleanup(struct xwl_screen *xwl_screen)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+
+    if (xwl_eglstream->display)
+        wl_eglstream_display_destroy(xwl_eglstream->display);
+    if (xwl_eglstream->controller)
+        wl_eglstream_controller_destroy(xwl_eglstream->controller);
+    if (xwl_eglstream->blit_prog) {
+        glDeleteProgram(xwl_eglstream->blit_prog);
+        glDeleteBuffers(1, &xwl_eglstream->blit_vbo);
+    }
+
+    free(xwl_eglstream);
+}
+
+static void
+xwl_eglstream_unref_pixmap_stream(struct xwl_pixmap *xwl_pixmap)
+{
+    struct xwl_screen *xwl_screen = xwl_pixmap->xwl_screen;
+
+    if (--xwl_pixmap->refcount >= 1)
+        return;
+
+    /* If we're using this stream in the current egl context, unbind it so the
+     * driver doesn't keep it around until the next eglMakeCurrent()
+     * don't have to keep it around until something else changes the surface
+     */
+    xwl_glamor_egl_make_current(xwl_screen);
+    if (eglGetCurrentSurface(EGL_READ) == xwl_pixmap->surface ||
+        eglGetCurrentSurface(EGL_DRAW) == xwl_pixmap->surface) {
+        eglMakeCurrent(xwl_screen->egl_display,
+                       EGL_NO_SURFACE, EGL_NO_SURFACE,
+                       xwl_screen->egl_context);
+    }
+
+    if (xwl_pixmap->surface)
+        eglDestroySurface(xwl_screen->egl_display, xwl_pixmap->surface);
+
+    eglDestroyStreamKHR(xwl_screen->egl_display, xwl_pixmap->stream);
+
+    wl_buffer_destroy(xwl_pixmap->buffer);
+    free(xwl_pixmap);
+}
+
+static Bool
+xwl_glamor_eglstream_destroy_pixmap(PixmapPtr pixmap)
+{
+    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
+
+    if (xwl_pixmap && pixmap->refcnt == 1)
+        xwl_eglstream_unref_pixmap_stream(xwl_pixmap);
+
+    return glamor_destroy_pixmap(pixmap);
+}
+
+static struct wl_buffer *
+xwl_glamor_eglstream_get_wl_buffer_for_pixmap(PixmapPtr pixmap,
+                                              unsigned short width,
+                                              unsigned short height,
+                                              Bool *created)
+{
+    /* XXX created? */
+    return xwl_pixmap_get(pixmap)->buffer;
+}
+
+static void
+xwl_eglstream_set_window_pixmap(WindowPtr window, PixmapPtr pixmap)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    struct xwl_eglstream_pending_stream *pending;
+
+    pending = xwl_eglstream_window_get_pending(window);
+    if (pending) {
+        /* The pixmap for this window has changed before the compositor
+         * finished attaching the consumer for the window's pixmap's original
+         * eglstream. A producer can no longer be attached, so the stream's
+         * useless
+         */
+        pending->is_valid = FALSE;
+
+        /* The compositor may still be using the stream, so we can't destroy
+         * it yet. We'll only have a guarantee that the stream is safe to
+         * destroy once we receive the pending wl_display_sync() for this
+         * stream
+         */
+        pending->xwl_pixmap->refcount++;
+    }
+
+    xwl_screen->screen->SetWindowPixmap = xwl_eglstream->SetWindowPixmap;
+    (*xwl_screen->screen->SetWindowPixmap)(window, pixmap);
+    xwl_eglstream->SetWindowPixmap = xwl_screen->screen->SetWindowPixmap;
+    xwl_screen->screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap;
+}
+
+/* Because we run asynchronously with our wayland compositor, it's possible
+ * that an X client event could cause us to begin creating a stream for a
+ * pixmap/window combo before the stream for the pixmap this window
+ * previously used has been fully initialized. An example:
+ *
+ * - Start processing X client events.
+ * - X window receives resize event, causing us to create a new pixmap and
+ *   begin creating the corresponding eglstream. This pixmap is known as
+ *   pixmap A.
+ * - X window receives another resize event, and again changes it's current
+ *   pixmap causing us to create another corresponding eglstream for the same
+ *   window. This pixmap is known as pixmap B.
+ * - Start handling events from the wayland compositor.
+ *
+ * Since both pixmap A and B will have scheduled wl_display_sync events to
+ * indicate when their respective streams are connected, we will receive each
+ * callback in the original order the pixmaps were created. This means the
+ * following would happen:
+ *
+ * - Receive pixmap A's stream callback, attach it's stream to the surface of
+ *   the window that just orphaned it.
+ * - Receive pixmap B's stream callback, fall over and fail because the
+ *   window's surface now incorrectly has pixmap A's stream attached to it.
+ *
+ * We work around this problem by keeping a queue of pending streams, and
+ * only allowing one queue entry to exist for each window. In the scenario
+ * listed above, this should happen:
+ *
+ * - Begin processing X events...
+ * - A window is resized, causing us to add an eglstream (known as eglstream
+ *   A) waiting for it's consumer to finish attachment to be added to the
+ *   queue.
+ * - Resize on same window happens. We invalidate the previously pending
+ *   stream and add another one to the pending queue (known as eglstream B).
+ * - Begin processing Wayland events...
+ * - Receive invalidated callback from compositor for eglstream A, destroy
+ *   stream.
+ * - Receive callback from compositor for eglstream B, create producer.
+ * - Success!
+ */
+static void
+xwl_eglstream_consumer_ready_callback(void *data,
+                                      struct wl_callback *callback,
+                                      uint32_t time)
+{
+    struct xwl_screen *xwl_screen = data;
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    struct xwl_pixmap *xwl_pixmap;
+    struct xwl_eglstream_pending_stream *pending;
+    Bool found = FALSE;
+
+    wl_callback_destroy(callback);
+
+    xorg_list_for_each_entry(pending, &xwl_eglstream->pending_streams, link) {
+        if (pending->cb == callback) {
+            found = TRUE;
+            break;
+        }
+    }
+    assert(found);
+
+    if (!pending->is_valid) {
+        xwl_eglstream_unref_pixmap_stream(pending->xwl_pixmap);
+        goto out;
+    }
+
+    xwl_glamor_egl_make_current(xwl_screen);
+
+    xwl_pixmap = pending->xwl_pixmap;
+    xwl_pixmap->surface = eglCreateStreamProducerSurfaceKHR(
+        xwl_screen->egl_display, xwl_eglstream->config,
+        xwl_pixmap->stream, (int[]) {
+            EGL_WIDTH,  pending->pixmap->drawable.width,
+            EGL_HEIGHT, pending->pixmap->drawable.height,
+            EGL_NONE
+        });
+
+    DebugF("eglstream: win %d completes eglstream for pixmap %p, congrats!\n",
+           pending->window->drawable.id, pending->pixmap);
+
+    xwl_eglstream_window_set_pending(pending->window, NULL);
+out:
+    xorg_list_del(&pending->link);
+    free(pending);
+}
+
+static const struct wl_callback_listener consumer_ready_listener = {
+    xwl_eglstream_consumer_ready_callback
+};
+
+static void
+xwl_eglstream_queue_pending_stream(struct xwl_screen *xwl_screen,
+                                   WindowPtr window, PixmapPtr pixmap)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    struct xwl_eglstream_pending_stream *pending_stream;
+
+#ifdef DEBUG
+    if (!xwl_eglstream_window_get_pending(window))
+        DebugF("eglstream: win %d begins new eglstream for pixmap %p\n",
+               window->drawable.id, pixmap);
+    else
+        DebugF("eglstream: win %d interrupts and replaces pending eglstream for pixmap %p\n",
+               window->drawable.id, pixmap);
+#endif
+
+    pending_stream = malloc(sizeof(*pending_stream));
+    pending_stream->window = window;
+    pending_stream->pixmap = pixmap;
+    pending_stream->xwl_pixmap = xwl_pixmap_get(pixmap);
+    pending_stream->is_valid = TRUE;
+    xorg_list_init(&pending_stream->link);
+    xorg_list_add(&pending_stream->link, &xwl_eglstream->pending_streams);
+    xwl_eglstream_window_set_pending(window, pending_stream);
+
+    pending_stream->cb = wl_display_sync(xwl_screen->display);
+    wl_callback_add_listener(pending_stream->cb, &consumer_ready_listener,
+                             xwl_screen);
+}
+
+static void
+xwl_eglstream_buffer_release_callback(void *data, struct wl_buffer *wl_buffer)
+{
+    xwl_eglstream_unref_pixmap_stream(data);
+}
+
+static const struct wl_buffer_listener xwl_eglstream_buffer_release_listener = {
+    xwl_eglstream_buffer_release_callback
+};
+
+static void
+xwl_eglstream_create_pending_stream(struct xwl_screen *xwl_screen,
+                                    WindowPtr window, PixmapPtr pixmap)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    struct xwl_pixmap *xwl_pixmap;
+    struct xwl_window *xwl_window = xwl_window_from_window(window);
+    struct wl_array stream_attribs;
+    int stream_fd = -1;
+
+    xwl_pixmap = calloc(sizeof(*xwl_pixmap), 1);
+    if (!xwl_pixmap)
+        FatalError("Not enough memory to create pixmap\n");
+    xwl_pixmap_set_private(pixmap, xwl_pixmap);
+
+    xwl_glamor_egl_make_current(xwl_screen);
+
+    xwl_pixmap->xwl_screen = xwl_screen;
+    xwl_pixmap->refcount = 1;
+    xwl_pixmap->stream = eglCreateStreamKHR(xwl_screen->egl_display, NULL);
+    stream_fd = eglGetStreamFileDescriptorKHR(xwl_screen->egl_display,
+                                              xwl_pixmap->stream);
+
+    wl_array_init(&stream_attribs);
+    xwl_pixmap->buffer =
+        wl_eglstream_display_create_stream(xwl_eglstream->display,
+                                           pixmap->drawable.width,
+                                           pixmap->drawable.height,
+                                           stream_fd,
+                                           WL_EGLSTREAM_HANDLE_TYPE_FD,
+                                           &stream_attribs);
+
+    wl_buffer_add_listener(xwl_pixmap->buffer,
+                           &xwl_eglstream_buffer_release_listener,
+                           xwl_pixmap);
+
+    wl_eglstream_controller_attach_eglstream_consumer(
+        xwl_eglstream->controller, xwl_window->surface, xwl_pixmap->buffer);
+
+    xwl_eglstream_queue_pending_stream(xwl_screen, window, pixmap);
+
+    close(stream_fd);
+}
+
+static Bool
+xwl_glamor_eglstream_allow_commits(struct xwl_window *xwl_window)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    struct xwl_eglstream_pending_stream *pending =
+        xwl_eglstream_window_get_pending(xwl_window->window);
+    PixmapPtr pixmap =
+        (*xwl_screen->screen->GetWindowPixmap)(xwl_window->window);
+    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
+
+    if (xwl_pixmap) {
+        if (pending) {
+            /* Wait for the compositor to finish connecting the consumer for
+             * this eglstream */
+            if (pending->is_valid)
+                return FALSE;
+
+            /* The pixmap for this window was changed before the compositor
+             * finished connecting the eglstream for the window's previous
+             * pixmap. Begin creating a new eglstream. */
+        } else {
+            return TRUE;
+        }
+    }
+
+    /* Glamor pixmap has no backing stream yet; begin making one and disallow
+     * commits until then
+     */
+    xwl_eglstream_create_pending_stream(xwl_screen, xwl_window->window,
+                                        pixmap);
+
+    return FALSE;
+}
+
+static void
+xwl_glamor_eglstream_post_damage(struct xwl_window *xwl_window,
+                                 PixmapPtr pixmap, RegionPtr region)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
+    BoxPtr box = RegionExtents(region);
+    EGLint egl_damage[] = {
+        box->x1,           box->y1,
+        box->x2 - box->x1, box->y2 - box->y1
+    };
+    GLint saved_vao;
+
+    /* Unbind the framebuffer BEFORE binding the EGLSurface, otherwise we
+     * won't actually draw to it
+     */
+    xwl_glamor_egl_make_current(xwl_screen);
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    if (eglGetCurrentSurface(EGL_READ) != xwl_pixmap->surface ||
+        eglGetCurrentSurface(EGL_DRAW) != xwl_pixmap->surface)
+        eglMakeCurrent(xwl_screen->egl_display,
+                       xwl_pixmap->surface, xwl_pixmap->surface,
+                       xwl_screen->egl_context);
+
+    /* Save current GL state */
+    glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao);
+
+    /* Setup our GL state */
+    glUseProgram(xwl_eglstream->blit_prog);
+    glViewport(0, 0, pixmap->drawable.width, pixmap->drawable.height);
+    glActiveTexture(GL_TEXTURE0);
+    glBindVertexArray(xwl_eglstream->blit_vao);
+    glBindTexture(GL_TEXTURE_2D, glamor_get_pixmap_texture(pixmap));
+
+    glUniform1i(xwl_eglstream->blit_is_rgba_pos,
+                pixmap->drawable.depth >= 32);
+
+    /* Blit rendered image into EGLStream surface */
+    glDrawBuffer(GL_BACK);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+    if (xwl_eglstream->have_egl_damage)
+        eglSwapBuffersWithDamageKHR(xwl_screen->egl_display,
+                                    xwl_pixmap->surface, egl_damage, 1);
+    else
+        eglSwapBuffers(xwl_screen->egl_display, xwl_pixmap->surface);
+
+    /* Restore previous state */
+    glBindVertexArray(saved_vao);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    /* After this we will hand off the eglstream's wl_buffer to the
+     * compositor, which will own it until it sends a release() event. */
+    xwl_pixmap->refcount++;
+}
+
+static void
+xwl_eglstream_display_handle_caps(void *data,
+                                  struct wl_eglstream_display *disp,
+                                  int32_t caps)
+{
+    xwl_eglstream_get(data)->display_caps = caps;
+}
+
+static void
+xwl_eglstream_display_handle_swapinterval_override(void *data,
+                                                   struct wl_eglstream_display *disp,
+                                                   int32_t swapinterval,
+                                                   struct wl_buffer *stream)
+{
+}
+
+const struct wl_eglstream_display_listener eglstream_display_listener = {
+    .caps = xwl_eglstream_display_handle_caps,
+    .swapinterval_override = xwl_eglstream_display_handle_swapinterval_override,
+};
+
+static void
+xwl_glamor_eglstream_init_wl_registry(struct xwl_screen *xwl_screen,
+                                      struct wl_registry *wl_registry,
+                                      const char *name,
+                                      uint32_t id, uint32_t version)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+
+    if (strcmp(name, "wl_eglstream_display") == 0) {
+        xwl_eglstream->display = wl_registry_bind(
+            wl_registry, id, &wl_eglstream_display_interface, version);
+
+        wl_eglstream_display_add_listener(xwl_eglstream->display,
+                                          &eglstream_display_listener,
+                                          xwl_screen);
+    } else if (strcmp(name, "wl_eglstream_controller") == 0) {
+        xwl_eglstream->controller = wl_registry_bind(
+            wl_registry, id, &wl_eglstream_controller_interface, version);
+    }
+}
+
+static inline void
+xwl_eglstream_init_shaders(struct xwl_screen *xwl_screen)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    GLint fs, vs, attrib;
+    GLuint vbo;
+
+    const char *blit_vs_src =
+        "attribute vec2 texcoord;\n"
+        "attribute vec2 position;\n"
+        "varying vec2 t;\n"
+        "void main() {\n"
+        "    t = texcoord;\n"
+        "    gl_Position = vec4(position, 0, 1);\n"
+        "}";
+
+    const char *blit_fs_src =
+        "varying vec2 t;\n"
+        "uniform sampler2D s;\n"
+        "uniform bool is_rgba;\n"
+        "void main() {\n"
+        "    if (is_rgba)\n"
+        "        gl_FragColor = texture2D(s, t);\n"
+        "    else\n"
+        "        gl_FragColor = vec4(texture2D(s, t).rgb, 1.0);\n"
+        "}";
+
+    static const float position[] = {
+        /* position */
+        -1, -1,
+         1, -1,
+         1,  1,
+        -1,  1,
+        /* texcoord */
+         0,  1,
+         1,  1,
+         1,  0,
+         0,  0,
+    };
+
+    vs = xwl_eglstream_compile_glsl_prog(GL_VERTEX_SHADER, blit_vs_src);
+    fs = xwl_eglstream_compile_glsl_prog(GL_FRAGMENT_SHADER, blit_fs_src);
+
+    xwl_eglstream->blit_prog = xwl_eglstream_build_glsl_prog(vs, fs);
+    glDeleteShader(vs);
+    glDeleteShader(fs);
+
+    /* Create the blitter's vao */
+    glGenVertexArrays(1, &xwl_eglstream->blit_vao);
+    glBindVertexArray(xwl_eglstream->blit_vao);
+
+    /* Set the data for both position and texcoord in the vbo */
+    glGenBuffers(1, &vbo);
+    glBindBuffer(GL_ARRAY_BUFFER, vbo);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
+    xwl_eglstream->blit_vbo = vbo;
+
+    /* Define each shader attribute's data location in our vbo */
+    attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "position");
+    glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0, NULL);
+    glEnableVertexAttribArray(attrib);
+
+    attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "texcoord");
+    glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0,
+                          (void*)(sizeof(float) * 8));
+    glEnableVertexAttribArray(attrib);
+
+    /* Save the location of uniforms we'll set later */
+    xwl_eglstream->blit_is_rgba_pos =
+        glGetUniformLocation(xwl_eglstream->blit_prog, "is_rgba");
+}
+
+static Bool
+xwl_glamor_eglstream_init_egl(struct xwl_screen *xwl_screen)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    EGLConfig config;
+    const EGLint attrib_list[] = {
+        EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
+        EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
+        EGL_CONTEXT_MAJOR_VERSION_KHR,
+        GLAMOR_GL_CORE_VER_MAJOR,
+        EGL_CONTEXT_MINOR_VERSION_KHR,
+        GLAMOR_GL_CORE_VER_MINOR,
+        EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
+        EGL_NONE
+    };
+    const EGLint config_attribs[] = {
+        EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+        EGL_RED_SIZE, 8,
+        EGL_GREEN_SIZE, 8,
+        EGL_BLUE_SIZE, 8,
+        EGL_ALPHA_SIZE, 8,
+        EGL_NONE,
+    };
+    int n;
+
+    xwl_screen->egl_display = glamor_egl_get_display(
+        EGL_PLATFORM_DEVICE_EXT, xwl_eglstream->egl_device);
+    if (!xwl_screen->egl_display)
+        goto error;
+
+    if (!eglInitialize(xwl_screen->egl_display, NULL, NULL)) {
+        xwl_screen->egl_display = NULL;
+        goto error;
+    }
+
+    eglChooseConfig(xwl_screen->egl_display, config_attribs, &config, 1, &n);
+    if (!n) {
+        ErrorF("No acceptable EGL configs found\n");
+        goto error;
+    }
+
+    xwl_eglstream->config = config;
+#if 0
+    xwl_screen->formats =
+        XWL_FORMAT_RGB565 | XWL_FORMAT_XRGB8888 | XWL_FORMAT_ARGB8888;
+#endif
+
+    eglBindAPI(EGL_OPENGL_API);
+    xwl_screen->egl_context = eglCreateContext(
+        xwl_screen->egl_display, config, EGL_NO_CONTEXT, attrib_list);
+    if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
+        ErrorF("Failed to create main EGL context: 0x%x\n", eglGetError());
+        goto error;
+    }
+
+    if (!eglMakeCurrent(xwl_screen->egl_display,
+                        EGL_NO_SURFACE, EGL_NO_SURFACE,
+                        xwl_screen->egl_context)) {
+        ErrorF("Failed to make EGL context current\n");
+        goto error;
+    }
+
+    xwl_eglstream->have_egl_damage =
+        epoxy_has_egl_extension(xwl_screen->egl_display,
+                                "EGL_KHR_swap_buffers_with_damage");
+    if (!xwl_eglstream->have_egl_damage)
+        ErrorF("Driver lacks EGL_KHR_swap_buffers_with_damage, performance "
+               "will be affected\n");
+
+    xwl_eglstream_init_shaders(xwl_screen);
+
+    return TRUE;
+error:
+    xwl_eglstream_cleanup(xwl_screen);
+    return FALSE;
+}
+
+static Bool
+xwl_glamor_eglstream_init_screen(struct xwl_screen *xwl_screen)
+{
+    struct xwl_eglstream_private *xwl_eglstream =
+        xwl_eglstream_get(xwl_screen);
+    ScreenPtr screen = xwl_screen->screen;
+
+    if (!xwl_eglstream->controller) {
+        ErrorF("No eglstream controller was exposed in the wayland registry. "
+               "This means your version of nvidia's EGL wayland libraries "
+               "are too old, as we require support for this.\n");
+        xwl_eglstream_cleanup(xwl_screen);
+        return FALSE;
+    }
+
+    /* We can just let glamor handle CreatePixmap */
+    screen->DestroyPixmap = xwl_glamor_eglstream_destroy_pixmap;
+
+    xwl_eglstream->SetWindowPixmap = screen->SetWindowPixmap;
+    screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap;
+
+    if (!dixRegisterPrivateKey(&xwl_eglstream_window_private_key,
+                               PRIVATE_WINDOW, 0))
+        return FALSE;
+
+    return TRUE;
+}
+
+static EGLDeviceEXT
+xwl_eglstream_get_device(struct xwl_screen *xwl_screen)
+{
+    void **devices = NULL;
+    const char *exts[] = {
+        "EGL_KHR_stream",
+        "EGL_KHR_stream_producer_eglsurface",
+    };
+    int num_devices, i;
+    EGLDeviceEXT device = EGL_NO_DEVICE_EXT;
+
+    /* No device specified by the user, so find one ourselves */
+    devices = xwl_glamor_egl_get_devices(&num_devices);
+    if (!devices)
+        goto out;
+
+    for (i = 0; i < num_devices; i++) {
+        if (xwl_glamor_egl_device_has_egl_extensions(devices[i], exts,
+                                                     ARRAY_SIZE(exts))) {
+            device = devices[i];
+            break;
+        }
+    }
+
+    free(devices);
+out:
+    if (!device)
+        ErrorF("glamor: No eglstream capable devices found\n");
+    return device;
+}
+
+Bool
+xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen)
+{
+    struct xwl_eglstream_private *xwl_eglstream;
+    EGLDeviceEXT egl_device;
+
+    egl_device = xwl_eglstream_get_device(xwl_screen);
+    if (egl_device == EGL_NO_DEVICE_EXT)
+        return FALSE;
+
+    if (!dixRegisterPrivateKey(&xwl_eglstream_private_key, PRIVATE_SCREEN, 0))
+        return FALSE;
+
+    xwl_eglstream = calloc(sizeof(*xwl_eglstream), 1);
+    if (!xwl_eglstream) {
+        ErrorF("Failed to allocate memory required to init eglstream support\n");
+        return FALSE;
+    }
+
+    dixSetPrivate(&xwl_screen->screen->devPrivates,
+                  &xwl_eglstream_private_key, xwl_eglstream);
+
+    xwl_eglstream->egl_device = egl_device;
+    xorg_list_init(&xwl_eglstream->pending_streams);
+
+    xwl_screen->egl_backend.init_egl = xwl_glamor_eglstream_init_egl;
+    xwl_screen->egl_backend.init_wl_registry = xwl_glamor_eglstream_init_wl_registry;
+    xwl_screen->egl_backend.init_screen = xwl_glamor_eglstream_init_screen;
+    xwl_screen->egl_backend.get_wl_buffer_for_pixmap = xwl_glamor_eglstream_get_wl_buffer_for_pixmap;
+    xwl_screen->egl_backend.post_damage = xwl_glamor_eglstream_post_damage;
+    xwl_screen->egl_backend.allow_commits = xwl_glamor_eglstream_allow_commits;
+
+    ErrorF("glamor: Using nvidia's eglstream interface, direct rendering impossible.\n");
+    ErrorF("glamor: Performance may be affected. Ask your vendor to support GBM!\n");
+    return TRUE;
+}
diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index c03ba3da1..4f7062599 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -146,11 +146,7 @@ xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo,
         return NULL;
     }
 
-    if (lastGLContext != xwl_screen->glamor_ctx) {
-        lastGLContext = xwl_screen->glamor_ctx;
-        xwl_screen->glamor_ctx->make_current(xwl_screen->glamor_ctx);
-    }
-
+    xwl_glamor_egl_make_current(xwl_screen);
     xwl_pixmap->bo = bo;
     xwl_pixmap->buffer = NULL;
     xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 7b24ce7e4..2bd16e7ac 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -32,7 +32,7 @@
 #include <glamor_context.h>
 
 static void
-xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
+glamor_egl_make_current(struct glamor_context *glamor_ctx)
 {
     eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
                    EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -43,6 +43,94 @@ xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
 }
 
 void
+xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen)
+{
+    if (lastGLContext == xwl_screen->glamor_ctx)
+        return;
+
+    lastGLContext = xwl_screen->glamor_ctx;
+    xwl_screen->glamor_ctx->make_current(xwl_screen->glamor_ctx);
+}
+
+Bool
+xwl_glamor_egl_supports_device_probing(void)
+{
+    return epoxy_has_egl() &&
+        epoxy_has_egl_extension(NULL, "EGL_EXT_device_base");
+}
+
+void **
+xwl_glamor_egl_get_devices(int *num_devices)
+{
+#ifdef XWL_HAS_EGLSTREAM
+    EGLDeviceEXT *devices;
+    Bool ret;
+    int drm_dev_count = 0;
+    int i;
+
+    /* Get the number of devices */
+    ret = eglQueryDevicesEXT(0, NULL, num_devices);
+    if (!ret || *num_devices < 1)
+        return NULL;
+
+    devices = calloc(*num_devices, sizeof(EGLDeviceEXT));
+    if (!devices)
+        return NULL;
+
+    ret = eglQueryDevicesEXT(*num_devices, devices, num_devices);
+    if (!ret)
+        goto error;
+
+    /* We're only ever going to care about devices that support
+     * EGL_EXT_device_drm, so filter out the ones that don't
+     */
+    for (i = 0; i < *num_devices; i++) {
+        const char *extension_str =
+            eglQueryDeviceStringEXT(devices[i], EGL_EXTENSIONS);
+
+        if (!epoxy_extension_in_string(extension_str, "EGL_EXT_device_drm"))
+            continue;
+
+        devices[drm_dev_count++] = devices[i];
+    }
+    if (!drm_dev_count)
+        goto error;
+
+    *num_devices = drm_dev_count;
+    devices = realloc(devices, sizeof(EGLDeviceEXT) * drm_dev_count);
+
+    return devices;
+
+error:
+    free(devices);
+#endif
+    return NULL;
+}
+
+Bool
+xwl_glamor_egl_device_has_egl_extensions(void *device,
+                                         const char **ext_list, size_t size)
+{
+    EGLDisplay egl_display;
+    int i;
+    Bool has_exts = TRUE;
+
+    egl_display = glamor_egl_get_display(EGL_PLATFORM_DEVICE_EXT, device);
+    if (!egl_display || !eglInitialize(egl_display, NULL, NULL))
+        return FALSE;
+
+    for (i = 0; i < size; i++) {
+        if (!epoxy_has_egl_extension(egl_display, ext_list[i])) {
+            has_exts = FALSE;
+            break;
+        }
+    }
+
+    eglTerminate(egl_display);
+    return has_exts;
+}
+
+void
 glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
@@ -50,7 +138,7 @@ glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
     glamor_ctx->ctx = xwl_screen->egl_context;
     glamor_ctx->display = xwl_screen->egl_display;
 
-    glamor_ctx->make_current = xwl_glamor_egl_make_current;
+    glamor_ctx->make_current = glamor_egl_make_current;
 
     xwl_screen->glamor_ctx = glamor_ctx;
 }
@@ -83,6 +171,27 @@ xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
     return NULL;
 }
 
+void
+xwl_glamor_post_damage(struct xwl_window *xwl_window,
+                       PixmapPtr pixmap, RegionPtr region)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+
+    if (xwl_screen->egl_backend.post_damage)
+        xwl_screen->egl_backend.post_damage(xwl_window, pixmap, region);
+}
+
+Bool
+xwl_glamor_allow_commits(struct xwl_window *xwl_window)
+{
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+
+    if (xwl_screen->egl_backend.allow_commits)
+        return xwl_screen->egl_backend.allow_commits(xwl_window);
+    else
+        return TRUE;
+}
+
 static Bool
 xwl_glamor_create_screen_resources(ScreenPtr screen)
 {
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index c41a8a2d1..07fdc7c18 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -509,5 +509,14 @@ static present_wnmd_info_rec xwl_present_info = {
 Bool
 xwl_present_init(ScreenPtr screen)
 {
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+
+    /*
+     * doesn't work with the streams backend. we don't have an explicit
+     * boolean for that, but we do know gbm doesn't fill in this hook...
+     */
+    if (xwl_screen->egl_backend.post_damage != NULL)
+        return FALSE;
+
     return present_wnmd_screen_init(screen, &xwl_present_info);
 }
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index a5b3df791..f7e2ce931 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -96,6 +96,9 @@ ddxUseMsg(void)
     ErrorF("-rootless              run rootless, requires wm support\n");
     ErrorF("-wm fd                 create X client for wm on given fd\n");
     ErrorF("-listen fd             add give fd as a listen socket\n");
+#ifdef XWL_HAS_EGLSTREAM
+    ErrorF("-eglstream             use eglstream backend for nvidia GPUs\n");
+#endif
 }
 
 int
@@ -114,6 +117,11 @@ ddxProcessArgument(int argc, char *argv[], int i)
     else if (strcmp(argv[i], "-shm") == 0) {
         return 1;
     }
+#ifdef XWL_HAS_EGLSTREAM
+    else if (strcmp(argv[i], "-eglstream") == 0) {
+        return 1;
+    }
+#endif
 
     return 0;
 }
@@ -678,6 +686,11 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
 #endif
         buffer = xwl_shm_pixmap_get_wl_buffer(pixmap);
 
+#ifdef XWL_HAS_GLAMOR
+    if (xwl_screen->glamor)
+        xwl_glamor_post_damage(xwl_window, pixmap, region);
+#endif
+
     wl_surface_attach(xwl_window->surface, buffer, 0, 0);
 
     /* Arbitrary limit to try to avoid flooding the Wayland
@@ -724,6 +737,11 @@ xwl_screen_post_damage(struct xwl_screen *xwl_screen)
         if (!xwl_window->allow_commits)
             continue;
 
+#ifdef XWL_HAS_GLAMOR
+        if (!xwl_glamor_allow_commits(xwl_window))
+            continue;
+#endif
+
         xwl_window_post_damage(xwl_window);
     }
 }
@@ -922,6 +940,9 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     struct xwl_screen *xwl_screen;
     Pixel red_mask, blue_mask, green_mask;
     int ret, bpc, green_bpc, i;
+#ifdef XWL_HAS_EGLSTREAM
+    Bool use_eglstreams = FALSE;
+#endif
 
     xwl_screen = calloc(1, sizeof *xwl_screen);
     if (xwl_screen == NULL)
@@ -964,10 +985,23 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         else if (strcmp(argv[i], "-shm") == 0) {
             xwl_screen->glamor = 0;
         }
+#ifdef XWL_HAS_EGLSTREAM
+        else if (strcmp(argv[i], "-eglstream") == 0) {
+            use_eglstreams = TRUE;
+        }
+#endif
     }
 
 #ifdef XWL_HAS_GLAMOR
     if (xwl_screen->glamor) {
+#ifdef XWL_HAS_EGLSTREAM
+        if (use_eglstreams) {
+            if (!xwl_glamor_init_eglstream(xwl_screen)) {
+                ErrorF("xwayland glamor: failed to setup eglstream backend, falling back to swaccel\n");
+                xwl_screen->glamor = 0;
+            }
+        } else
+#endif
         if (!xwl_glamor_init_gbm(xwl_screen)) {
             ErrorF("xwayland glamor: failed to setup GBM backend, falling back to sw accel\n");
             xwl_screen->glamor = 0;
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index 73f9c6a99..985ba9d64 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -141,6 +141,21 @@ struct xwl_screen {
                                                       unsigned short width,
                                                       unsigned short height,
                                                       Bool *created);
+
+        /* Called by Xwayland to perform any pre-wl_surface damage routines
+         * that are required by the backend. If your backend is poorly
+         * designed and lacks the ability to render directly to a surface,
+         * you should implement blitting from the glamor pixmap to the wayland
+         * pixmap here. Otherwise, this callback is optional.
+         */
+        void (*post_damage)(struct xwl_window *xwl_window,
+                            PixmapPtr pixmap, RegionPtr region);
+
+        /* Called by Xwayland to confirm with the egl backend that the given
+         * pixmap is completely setup and ready for display on-screen. This
+         * callback is optional.
+         */
+        Bool (*allow_commits)(struct xwl_window *xwl_window);
     } egl_backend;
 
     struct glamor_context *glamor_ctx;
@@ -412,6 +427,9 @@ void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
                                  struct wl_registry *registry,
                                  uint32_t id, const char *interface,
                                  uint32_t version);
+void xwl_glamor_post_damage(struct xwl_window *xwl_window,
+                            PixmapPtr pixmap, RegionPtr region);
+Bool xwl_glamor_allow_commits(struct xwl_window *xwl_window);
 
 #ifdef GLAMOR_HAS_GBM
 Bool xwl_present_init(ScreenPtr screen);
@@ -423,6 +441,13 @@ void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen);
 void xwl_output_get_xdg_output(struct xwl_output *xwl_output);
 void xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen);
 
+void xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen);
+Bool xwl_glamor_egl_supports_device_probing(void);
+void **xwl_glamor_egl_get_devices(int *num_devices);
+Bool xwl_glamor_egl_device_has_egl_extensions(void *device,
+                                              const char **ext_list,
+                                              size_t size);
+
 #ifdef XV
 /* glamor Xv Adaptor */
 Bool xwl_glamor_xv_init(ScreenPtr pScreen);
@@ -434,6 +459,20 @@ void xwlVidModeExtensionInit(void);
 
 #ifdef GLAMOR_HAS_GBM
 Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen);
+#else
+static inline Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
+{
+    return FALSE;
+}
+#endif
+
+#ifdef XWL_HAS_EGLSTREAM
+Bool xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen);
+#else
+static inline Bool xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen)
+{
+    return FALSE;
+}
 #endif
 
 #endif
diff --git a/include/meson.build b/include/meson.build
index d44ec4b97..06affc327 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -79,9 +79,9 @@ conf_data.set('GLXEXT', build_glx)
 conf_data.set('GLAMOR', build_glamor)
 conf_data.set('GLAMOR_HAS_GBM', gbm_dep.found())
 conf_data.set('GLAMOR_HAS_GBM_LINEAR',
-              gbm_dep.found() and gbm_dep.version().version_compare('>= 10.6'))
+              build_glamor and gbm_dep.found() and gbm_dep.version().version_compare('>= 10.6'))
 conf_data.set('GBM_BO_WITH_MODIFIERS',
-              gbm_dep.found() and gbm_dep.version().version_compare('>= 17.1'))
+              build_glamor and gbm_dep.found() and gbm_dep.version().version_compare('>= 17.1'))
 
 conf_data.set_quoted('SERVER_MISC_CONFIG_PATH', serverconfigdir)
 conf_data.set_quoted('PROJECTROOT', get_option('prefix'))
@@ -360,7 +360,8 @@ configure_file(output : 'xwin-config.h',
                configuration : xwin_data)
 
 xwayland_data = configuration_data()
-xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and gbm_dep.found())
+xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and (gbm_dep.found() or build_eglstream))
+xwayland_data.set('XWL_HAS_EGLSTREAM', build_eglstream)
 
 configure_file(output : 'xwayland-config.h',
                input : 'xwayland-config.h.meson.in',
diff --git a/include/xwayland-config.h.in b/include/xwayland-config.h.in
index 333b53f23..9695aae6b 100644
--- a/include/xwayland-config.h.in
+++ b/include/xwayland-config.h.in
@@ -7,4 +7,7 @@
 /* Build glamor support for Xwayland */
 #undef XWL_HAS_GLAMOR
 
+/* Build eglstream support for Xwayland */
+#undef XWL_HAS_EGLSTREAM
+
 #endif /* _XWAYLAND_CONFIG_H_ */
diff --git a/include/xwayland-config.h.meson.in b/include/xwayland-config.h.meson.in
index 6d37e8bf6..0943ff57d 100644
--- a/include/xwayland-config.h.meson.in
+++ b/include/xwayland-config.h.meson.in
@@ -6,3 +6,6 @@
 
 /* Build glamor support for Xwayland */
 #mesondefine XWL_HAS_GLAMOR
+
+/* Build eglstream support for Xwayland */
+#mesondefine XWL_HAS_EGLSTREAM
diff --git a/meson.build b/meson.build
index 5e794ccd0..a2e6a3d29 100644
--- a/meson.build
+++ b/meson.build
@@ -287,6 +287,21 @@ if build_glamor
     epoxy_dep = dependency('epoxy', required: false)
 endif
 
+eglstream_option = get_option('xwayland_eglstream')
+if build_xwayland and build_glamor
+    eglstream_dep = dependency('wayland-eglstream-protocols', required:false)
+    if eglstream_option == 'auto'
+        build_eglstream = eglstream_dep.found()
+    else
+        build_eglstream = eglstream_option == 'true'
+        if build_eglstream and not eglstream_dep.found()
+            error('glamor EGLStream support requested, but wayland-eglstream-protocols not found')
+        endif
+    endif
+else
+    build_eglstream = false
+endif
+
 # XXX: Add more sha1 options, because Linux is about choice
 sha1_dep = nettle_dep
 
diff --git a/meson_options.txt b/meson_options.txt
index 5c7be0e26..a296838a1 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,6 +6,8 @@ option('xwayland', type: 'combo', choices: ['true', 'false', 'auto'], value: 'au
        description: 'Enable XWayland X server')
 option('glamor', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
        description: 'Enable glamor (default yes for Xorg/Xwayland builds)')
+option('xwayland_eglstream', type: 'combo', choices: ['true', 'false', 'auto'],
+       value: 'auto', description: 'Enable EGLStream support for glamor on Xwayland')
 option('xnest', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
        description: 'Enable Xnest nested X server')
 option('dmx', type: 'boolean', value: false,
commit 994f781007079176e0247ee64af5696d34debcde
Author: Lyude Paul <lyude at redhat.com>
Date:   Fri Apr 20 14:38:04 2018 -0400

    xwayland: Add xwayland-config.h
    
    Just a small autogenerated header that will soon contain more then just
    one macro.
    
    Signed-off-by: Lyude Paul <lyude at redhat.com>
    Reviewed-by: Daniel Stone <daniels at collabora.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/configure.ac b/configure.ac
index aa361d9a9..b20eda12f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,8 @@ dnl xkb-config.h covers XKB for the Xorg and Xnest DDXs.
 AC_CONFIG_HEADERS(include/xkb-config.h)
 dnl xwin-config.h covers the XWin DDX.
 AC_CONFIG_HEADERS(include/xwin-config.h)
+dnl xwayland-config.h covers Xwayland.
+AC_CONFIG_HEADERS(include/xwayland-config.h)
 dnl version-config.h covers the version numbers so they can be bumped without
 dnl forcing an entire recompile.x
 AC_CONFIG_HEADERS(include/version-config.h)
@@ -2378,6 +2380,11 @@ if test "x$XWAYLAND" = xyes; then
 		AC_MSG_ERROR([Xwayland build explicitly requested, but required modules not found.])
 	fi
 
+	if test "x$GLAMOR" = xyes && test "x$GBM" = xyes; then
+		AC_DEFINE(XWL_HAS_GLAMOR, 1,
+			  [Build xwayland with glamor support])
+	fi
+
 	XWAYLAND_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB $DIX_LIB $OS_LIB"
 	XWAYLAND_SYS_LIBS="$XWAYLANDMODULES_LIBS $GLX_SYS_LIBS"
 	AC_SUBST([XWAYLAND_LIBS])
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index b37b62c78..a5b3df791 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -668,7 +668,7 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
     region = DamageRegion(xwl_window->damage);
     pixmap = (*xwl_screen->screen->GetWindowPixmap) (xwl_window->window);
 
-#ifdef GLAMOR_HAS_GBM
+#ifdef XWL_HAS_GLAMOR
     if (xwl_screen->glamor)
         buffer = xwl_glamor_pixmap_get_wl_buffer(pixmap,
                                                  pixmap->drawable.width,
@@ -754,7 +754,7 @@ registry_global(void *data, struct wl_registry *registry, uint32_t id,
             wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, 1);
         xwl_screen_init_xdg_output(xwl_screen);
     }
-#ifdef GLAMOR_HAS_GBM
+#ifdef XWL_HAS_GLAMOR
     else if (xwl_screen->glamor) {
         xwl_glamor_init_wl_registry(xwl_screen, registry, id, interface,
                                     version);
@@ -938,7 +938,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     dixSetPrivate(&pScreen->devPrivates, &xwl_screen_private_key, xwl_screen);
     xwl_screen->screen = pScreen;
 
-#ifdef GLAMOR_HAS_GBM
+#ifdef XWL_HAS_GLAMOR
     xwl_screen->glamor = 1;
 #endif
 
@@ -966,12 +966,14 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         }
     }
 
+#ifdef XWL_HAS_GLAMOR
     if (xwl_screen->glamor) {
         if (!xwl_glamor_init_gbm(xwl_screen)) {
             ErrorF("xwayland glamor: failed to setup GBM backend, falling back to sw accel\n");
             xwl_screen->glamor = 0;
         }
     }
+#endif
 
     /* In rootless mode, we don't have any screen storage, and the only
      * rendering should be to redirected mode. */
@@ -1055,7 +1057,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     if (!xwl_screen_init_cursor(xwl_screen))
         return FALSE;
 
-#ifdef GLAMOR_HAS_GBM
+#ifdef XWL_HAS_GLAMOR
     if (xwl_screen->glamor && !xwl_glamor_init(xwl_screen)) {
         ErrorF("Failed to initialize glamor, falling back to sw\n");
         xwl_screen->glamor = 0;
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index 82cbfc9d3..73f9c6a99 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -26,7 +26,7 @@
 #ifndef XWAYLAND_H
 #define XWAYLAND_H
 
-#include <dix-config.h>
+#include <xwayland-config.h>
 
 #include <stdio.h>
 #include <unistd.h>
diff --git a/include/meson.build b/include/meson.build
index e454b21b9..d44ec4b97 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -359,6 +359,13 @@ configure_file(output : 'xwin-config.h',
                input : 'xwin-config.h.meson.in',
                configuration : xwin_data)
 
+xwayland_data = configuration_data()
+xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and gbm_dep.found())
+
+configure_file(output : 'xwayland-config.h',
+               input : 'xwayland-config.h.meson.in',
+               configuration : xwayland_data)
+
 if build_xorg
     install_data(
         [
diff --git a/include/xwayland-config.h.in b/include/xwayland-config.h.in
new file mode 100644
index 000000000..333b53f23
--- /dev/null
+++ b/include/xwayland-config.h.in
@@ -0,0 +1,10 @@
+/* xwayland-config.h.in: not at all generated. */
+#ifndef _XWAYLAND_CONFIG_H_
+#define _XWAYLAND_CONFIG_H_
+
+#include <dix-config.h>
+
+/* Build glamor support for Xwayland */
+#undef XWL_HAS_GLAMOR
+
+#endif /* _XWAYLAND_CONFIG_H_ */
diff --git a/include/xwayland-config.h.meson.in b/include/xwayland-config.h.meson.in
new file mode 100644
index 000000000..6d37e8bf6
--- /dev/null
+++ b/include/xwayland-config.h.meson.in
@@ -0,0 +1,8 @@
+/* xwayland-config.h.meson.in: not at all generated */
+
+#pragma once
+
+#include <dix-config.h>
+
+/* Build glamor support for Xwayland */
+#mesondefine XWL_HAS_GLAMOR
commit 1545e2dbadcb147d7d52b546d053149de866a031
Author: Lyude Paul <lyude at redhat.com>
Date:   Fri Apr 20 14:38:03 2018 -0400

    xwayland: Decouple GBM from glamor
    
    This takes all of the gbm related code in wayland-glamor.c and moves it
    into it's own EGL backend for Xwayland, xwayland-glamor-gbm.c.
    Additionally, we add the egl_backend struct into xwl_screen in order to
    provide hooks for alternative EGL backends such as nvidia's EGLStreams.
    
    Signed-off-by: Lyude Paul <lyude at redhat.com>
    Reviewed-by: Lyude Paul <lyude at redhat.com>
    Reviewed-by: Daniel Stone <daniels at collabora.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>

diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am
index 80d3a1f19..3fd980d0e 100644
--- a/hw/xwayland/Makefile.am
+++ b/hw/xwayland/Makefile.am
@@ -35,6 +35,7 @@ Xwayland_built_sources =
 if GLAMOR_EGL
 Xwayland_SOURCES += 				\
 	xwayland-glamor.c			\
+	xwayland-glamor-gbm.c			\
 	xwayland-present.c
 if XV
 Xwayland_SOURCES += 				\
diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index 8d178825e..ef4379aab 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -52,7 +52,11 @@ srcs += code.process(dmabuf_xml)
 
 xwayland_glamor = []
 if gbm_dep.found()
-    srcs += [ 'xwayland-glamor.c', 'xwayland-present.c' ]
+    srcs += [
+        'xwayland-glamor.c',
+        'xwayland-glamor-gbm.c',
+        'xwayland-present.c',
+    ]
     if build_xv
         srcs += 'xwayland-glamor-xv.c'
     endif
diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
new file mode 100644
index 000000000..c03ba3da1
--- /dev/null
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright © 2011-2014 Intel Corporation
+ * Copyright © 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including
+ * the next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Lyude Paul <lyude at redhat.com>
+ *
+ */
+
+#include "xwayland.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <xf86drm.h>
+#include <drm_fourcc.h>
+
+#define MESA_EGL_NO_X11_HEADERS
+#include <gbm.h>
+#include <glamor_egl.h>
+
+#include <glamor.h>
+#include <glamor_context.h>
+#include <dri3.h>
+#include "drm-client-protocol.h"
+
+struct xwl_gbm_private {
+    char *device_name;
+    struct gbm_device *gbm;
+    struct wl_drm *drm;
+    struct zwp_linux_dmabuf_v1 *dmabuf;
+    int drm_fd;
+    int fd_render_node;
+    Bool drm_authenticated;
+    uint32_t capabilities;
+    int dmabuf_capable;
+};
+
+struct xwl_pixmap {
+    struct wl_buffer *buffer;
+    EGLImage image;
+    unsigned int texture;
+    struct gbm_bo *bo;
+};
+
+static DevPrivateKeyRec xwl_gbm_private_key;
+static DevPrivateKeyRec xwl_auth_state_private_key;
+
+static inline struct xwl_gbm_private *
+xwl_gbm_get(struct xwl_screen *xwl_screen)
+{
+    return dixLookupPrivate(&xwl_screen->screen->devPrivates,
+                            &xwl_gbm_private_key);
+}
+
+static uint32_t
+gbm_format_for_depth(int depth)
+{
+    switch (depth) {
+    case 16:
+        return GBM_FORMAT_RGB565;
+    case 24:
+        return GBM_FORMAT_XRGB8888;
+    case 30:
+        return GBM_FORMAT_ARGB2101010;
+    default:
+        ErrorF("unexpected depth: %d\n", depth);
+    case 32:
+        return GBM_FORMAT_ARGB8888;
+    }
+}
+
+static uint32_t
+wl_drm_format_for_depth(int depth)
+{
+    switch (depth) {
+    case 15:
+        return WL_DRM_FORMAT_XRGB1555;
+    case 16:
+        return WL_DRM_FORMAT_RGB565;
+    case 24:
+        return WL_DRM_FORMAT_XRGB8888;
+    case 30:
+        return WL_DRM_FORMAT_ARGB2101010;
+    default:
+        ErrorF("unexpected depth: %d\n", depth);
+    case 32:
+        return WL_DRM_FORMAT_ARGB8888;
+    }
+}
+
+static char
+is_fd_render_node(int fd)
+{
+    struct stat render;
+
+    if (fstat(fd, &render))
+        return 0;
+    if (!S_ISCHR(render.st_mode))
+        return 0;
+    if (render.st_rdev & 0x80)
+        return 1;
+
+    return 0;
+}
+
+static PixmapPtr
+xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo,
+                                    int depth)
+{
+    PixmapPtr pixmap;
+    struct xwl_pixmap *xwl_pixmap;
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+
+    xwl_pixmap = malloc(sizeof *xwl_pixmap);
+    if (xwl_pixmap == NULL)
+        return NULL;
+
+    pixmap = glamor_create_pixmap(screen,
+                                  gbm_bo_get_width(bo),
+                                  gbm_bo_get_height(bo),
+                                  depth,
+                                  GLAMOR_CREATE_PIXMAP_NO_TEXTURE);
+    if (!pixmap) {
+        free(xwl_pixmap);
+        return NULL;
+    }
+
+    if (lastGLContext != xwl_screen->glamor_ctx) {
+        lastGLContext = xwl_screen->glamor_ctx;
+        xwl_screen->glamor_ctx->make_current(xwl_screen->glamor_ctx);
+    }
+
+    xwl_pixmap->bo = bo;
+    xwl_pixmap->buffer = NULL;
+    xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
+                                          xwl_screen->egl_context,
+                                          EGL_NATIVE_PIXMAP_KHR,
+                                          xwl_pixmap->bo, NULL);
+
+    glGenTextures(1, &xwl_pixmap->texture);
+    glBindTexture(GL_TEXTURE_2D, xwl_pixmap->texture);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, xwl_pixmap->image);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    xwl_pixmap_set_private(pixmap, xwl_pixmap);
+
+    glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture);
+    glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
+
+    return pixmap;
+}
+
+static PixmapPtr
+xwl_glamor_gbm_create_pixmap(ScreenPtr screen,
+                             int width, int height, int depth,
+                             unsigned int hint)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    struct gbm_bo *bo;
+
+    if (width > 0 && height > 0 && depth >= 15 &&
+        (hint == 0 ||
+         hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP ||
+         hint == CREATE_PIXMAP_USAGE_SHARED)) {
+        uint32_t format = gbm_format_for_depth(depth);
+
+#ifdef GBM_BO_WITH_MODIFIERS
+        if (xwl_gbm->dmabuf_capable) {
+            uint32_t num_modifiers;
+            uint64_t *modifiers = NULL;
+
+            glamor_get_modifiers(screen, format, &num_modifiers, &modifiers);
+            bo = gbm_bo_create_with_modifiers(xwl_gbm->gbm, width, height,
+                                              format, modifiers, num_modifiers);
+            free(modifiers);
+        }
+        else
+#endif
+        {
+            bo = gbm_bo_create(xwl_gbm->gbm, width, height, format,
+                               GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+        }
+
+        if (bo)
+            return xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth);
+    }
+
+    return glamor_create_pixmap(screen, width, height, depth, hint);
+}
+
+static Bool
+xwl_glamor_gbm_destroy_pixmap(PixmapPtr pixmap)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
+    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
+
+    if (xwl_pixmap && pixmap->refcnt == 1) {
+        if (xwl_pixmap->buffer)
+            wl_buffer_destroy(xwl_pixmap->buffer);
+
+        eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image);
+        if (xwl_pixmap->bo)
+           gbm_bo_destroy(xwl_pixmap->bo);
+        free(xwl_pixmap);
+    }
+
+    return glamor_destroy_pixmap(pixmap);
+}
+
+static struct wl_buffer *
+xwl_glamor_gbm_get_wl_buffer_for_pixmap(PixmapPtr pixmap,
+                                        unsigned short width,
+                                        unsigned short height,
+                                        Bool *created)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
+    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    int prime_fd;
+    int num_planes;
+    uint32_t strides[4];
+    uint32_t offsets[4];
+    uint64_t modifier;
+    int i;
+
+    if (xwl_pixmap->buffer) {
+        /* Buffer already exists. Return it and inform caller if interested. */
+        if (created)
+            *created = FALSE;
+        return xwl_pixmap->buffer;
+    }
+
+    /* Buffer does not exist yet. Create now and inform caller if interested. */
+    if (created)
+        *created = TRUE;
+
+    if (!xwl_pixmap->bo)
+       return NULL;
+
+    prime_fd = gbm_bo_get_fd(xwl_pixmap->bo);
+    if (prime_fd == -1)
+        return NULL;
+
+#ifdef GBM_BO_WITH_MODIFIERS
+    num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
+    modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
+    for (i = 0; i < num_planes; i++) {
+        strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
+        offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
+    }
+#else
+    num_planes = 1;
+    modifier = DRM_FORMAT_MOD_INVALID;
+    strides[0] = gbm_go_get_stride(xwl_pixmap->bo);
+    offsets[0] = 0;
+#endif
+
+    if (xwl_gbm->dmabuf && modifier != DRM_FORMAT_MOD_INVALID) {
+        struct zwp_linux_buffer_params_v1 *params;
+
+        params = zwp_linux_dmabuf_v1_create_params(xwl_gbm->dmabuf);
+        for (i = 0; i < num_planes; i++) {
+            zwp_linux_buffer_params_v1_add(params, prime_fd, i,
+                                           offsets[i], strides[i],
+                                           modifier >> 32, modifier & 0xffffffff);
+        }
+
+        xwl_pixmap->buffer =
+           zwp_linux_buffer_params_v1_create_immed(params, width, height,
+                                                   wl_drm_format_for_depth(pixmap->drawable.depth),
+                                                   0);
+        zwp_linux_buffer_params_v1_destroy(params);
+    } else if (num_planes == 1) {
+        xwl_pixmap->buffer =
+            wl_drm_create_prime_buffer(xwl_gbm->drm, prime_fd, width, height,
+                                       wl_drm_format_for_depth(pixmap->drawable.depth),
+                                       0, gbm_bo_get_stride(xwl_pixmap->bo),
+                                       0, 0,
+                                       0, 0);
+    }
+
+    close(prime_fd);
+    return xwl_pixmap->buffer;
+}
+
+static void
+xwl_glamor_gbm_cleanup(struct xwl_screen *xwl_screen)
+{
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    if (xwl_gbm->device_name)
+        free(xwl_gbm->device_name);
+    if (xwl_gbm->drm_fd)
+        close(xwl_gbm->drm_fd);
+    if (xwl_gbm->drm)
+        wl_drm_destroy(xwl_gbm->drm);
+    if (xwl_gbm->gbm)
+        gbm_device_destroy(xwl_gbm->gbm);
+
+    free(xwl_gbm);
+}
+
+struct xwl_auth_state {
+    int fd;
+    ClientPtr client;
+    struct wl_callback *callback;
+};
+
+static void
+free_xwl_auth_state(ClientPtr pClient, struct xwl_auth_state *state)
+{
+    dixSetPrivate(&pClient->devPrivates, &xwl_auth_state_private_key, NULL);
+    if (state) {
+        wl_callback_destroy(state->callback);
+        free(state);
+    }
+}
+
+static void
+xwl_auth_state_client_callback(CallbackListPtr *pcbl, void *unused, void *data)
+{
+    NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
+    ClientPtr pClient = clientinfo->client;
+    struct xwl_auth_state *state;
+
+    switch (pClient->clientState) {
+    case ClientStateGone:
+    case ClientStateRetained:
+        state = dixLookupPrivate(&pClient->devPrivates,
+                                 &xwl_auth_state_private_key);
+        free_xwl_auth_state(pClient, state);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
+{
+    struct xwl_auth_state *state = data;
+    ClientPtr client = state->client;
+
+    /* if the client is gone, the callback is cancelled so it's safe to
+     * assume the client is still in ClientStateRunning at this point...
+     */
+    dri3_send_open_reply(client, state->fd);
+    AttendClient(client);
+    free_xwl_auth_state(client, state);
+}
+
+static const struct wl_callback_listener sync_listener = {
+   sync_callback
+};
+
+static int
+xwl_dri3_open_client(ClientPtr client,
+                     ScreenPtr screen,
+                     RRProviderPtr provider,
+                     int *pfd)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    struct xwl_auth_state *state;
+    drm_magic_t magic;
+    int fd;
+
+    fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
+    if (fd < 0)
+        return BadAlloc;
+    if (xwl_gbm->fd_render_node) {
+        *pfd = fd;
+        return Success;
+    }
+
+    state = malloc(sizeof *state);
+    if (state == NULL) {
+        close(fd);
+        return BadAlloc;
+    }
+
+    state->client = client;
+    state->fd = fd;
+
+    if (drmGetMagic(state->fd, &magic) < 0) {
+        close(state->fd);
+        free(state);
+        return BadMatch;
+    }
+
+    wl_drm_authenticate(xwl_gbm->drm, magic);
+    state->callback = wl_display_sync(xwl_screen->display);
+    wl_callback_add_listener(state->callback, &sync_listener, state);
+    dixSetPrivate(&client->devPrivates, &xwl_auth_state_private_key, state);
+
+    IgnoreClient(client);
+
+    return Success;
+}
+
+_X_EXPORT PixmapPtr
+glamor_pixmap_from_fds(ScreenPtr screen, CARD8 num_fds, const int *fds,
+                       CARD16 width, CARD16 height,
+                       const CARD32 *strides, const CARD32 *offsets,
+                       CARD8 depth, CARD8 bpp, uint64_t modifier)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    struct gbm_bo *bo = NULL;
+    PixmapPtr pixmap;
+    int i;
+
+    if (width == 0 || height == 0 || num_fds == 0 ||
+        depth < 15 || bpp != BitsPerPixel(depth) ||
+        strides[0] < width * bpp / 8)
+       goto error;
+
+    if (xwl_gbm->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) {
+#ifdef GBM_BO_WITH_MODIFIERS
+       struct gbm_import_fd_modifier_data data;
+
+       data.width = width;
+       data.height = height;
+       data.num_fds = num_fds;
+       data.format = gbm_format_for_depth(depth);
+       data.modifier = modifier;
+       for (i = 0; i < num_fds; i++) {
+          data.fds[i] = fds[i];
+          data.strides[i] = strides[i];
+          data.offsets[i] = offsets[i];
+       }
+       bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, 0);
+#endif
+    } else if (num_fds == 1) {
+       struct gbm_import_fd_data data;
+
+       data.fd = fds[0];
+       data.width = width;
+       data.height = height;
+       data.stride = strides[0];
+       data.format = gbm_format_for_depth(depth);
+       bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD, &data,
+             GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+    } else {
+       goto error;
+    }
+
+    if (bo == NULL)
+       goto error;
+
+    pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth);
+    if (pixmap == NULL) {
+       gbm_bo_destroy(bo);
+       goto error;
+    }
+
+    return pixmap;
+
+error:
+    return NULL;
+}
+
+_X_EXPORT int
+glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
+                           uint32_t *strides, uint32_t *offsets,
+                           uint64_t *modifier)
+{
+    struct xwl_pixmap *xwl_pixmap;
+#ifdef GBM_BO_WITH_MODIFIERS
+    uint32_t num_fds;
+    int i;
+#endif
+
+    xwl_pixmap = xwl_pixmap_get(pixmap);
+
+    if (!xwl_pixmap->bo)
+       return 0;
+
+#ifdef GBM_BO_WITH_MODIFIERS
+    num_fds = gbm_bo_get_plane_count(xwl_pixmap->bo);
+    *modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
+
+    for (i = 0; i < num_fds; i++) {
+        fds[i] = gbm_bo_get_fd(xwl_pixmap->bo);
+        strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
+        offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
+    }
+
+    return num_fds;
+#else
+    *modifier = DRM_FORMAT_MOD_INVALID;
+    fds[0] = gbm_bo_get_fd(xwl_pixmap->bo);
+    strides[0] = gbm_bo_get_stride(xwl_pixmap->bo);
+    offsets[0] = 0;
+    return 1;
+#endif
+}
+
+_X_EXPORT Bool
+glamor_get_formats(ScreenPtr screen,
+                   CARD32 *num_formats, CARD32 **formats)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    int i;
+
+    /* Explicitly zero the count as the caller may ignore the return value */
+    *num_formats = 0;
+
+    if (!xwl_gbm->dmabuf_capable || !xwl_gbm->dmabuf)
+        return FALSE;
+
+    if (xwl_screen->num_formats == 0)
+       return TRUE;
+
+    *formats = calloc(xwl_screen->num_formats, sizeof(CARD32));
+    if (*formats == NULL)
+        return FALSE;
+
+    for (i = 0; i < xwl_screen->num_formats; i++)
+       (*formats)[i] = xwl_screen->formats[i].format;
+    *num_formats = xwl_screen->num_formats;
+
+    return TRUE;
+}
+
+_X_EXPORT Bool
+glamor_get_modifiers(ScreenPtr screen, CARD32 format,
+                     CARD32 *num_modifiers, uint64_t **modifiers)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    struct xwl_format *xwl_format = NULL;
+    int i;
+
+    /* Explicitly zero the count as the caller may ignore the return value */
+    *num_modifiers = 0;
+
+    if (!xwl_gbm->dmabuf_capable || !xwl_gbm->dmabuf)
+        return FALSE;
+
+    if (xwl_screen->num_formats == 0)
+       return TRUE;
+
+    for (i = 0; i < xwl_screen->num_formats; i++) {
+       if (xwl_screen->formats[i].format == format) {
+          xwl_format = &xwl_screen->formats[i];
+          break;
+       }
+    }
+
+    if (!xwl_format)
+        return FALSE;
+
+    *modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t));
+    if (*modifiers == NULL)
+        return FALSE;
+
+    for (i = 0; i < xwl_format->num_modifiers; i++)
+       (*modifiers)[i] = xwl_format->modifiers[i];
+    *num_modifiers = xwl_format->num_modifiers;
+
+    return TRUE;
+}
+
+static const dri3_screen_info_rec xwl_dri3_info = {
+    .version = 2,
+    .open = NULL,
+    .pixmap_from_fds = glamor_pixmap_from_fds,
+    .fds_from_pixmap = glamor_fds_from_pixmap,
+    .open_client = xwl_dri3_open_client,
+    .get_formats = glamor_get_formats,
+    .get_modifiers = glamor_get_modifiers,
+    .get_drawable_modifiers = glamor_get_drawable_modifiers,
+};
+
+static void
+xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device)
+{
+   struct xwl_screen *xwl_screen = data;
+   struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+   drm_magic_t magic;
+
+   xwl_gbm->device_name = strdup(device);
+   if (!xwl_gbm->device_name) {
+       xwl_glamor_gbm_cleanup(xwl_screen);
+       return;
+   }
+
+   xwl_gbm->drm_fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
+   if (xwl_gbm->drm_fd == -1) {
+       ErrorF("wayland-egl: could not open %s (%s)\n",
+              xwl_gbm->device_name, strerror(errno));
+       xwl_glamor_gbm_cleanup(xwl_screen);
+       return;
+   }
+
+   if (is_fd_render_node(xwl_gbm->drm_fd)) {
+       xwl_gbm->fd_render_node = 1;
+       xwl_screen->expecting_event--;
+   } else {
+       drmGetMagic(xwl_gbm->drm_fd, &magic);
+       wl_drm_authenticate(xwl_gbm->drm, magic);
+   }
+}
+
+static void
+xwl_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
+{
+}
+
+static void
+xwl_drm_handle_authenticated(void *data, struct wl_drm *drm)
+{
+    struct xwl_screen *xwl_screen = data;
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    xwl_gbm->drm_authenticated = TRUE;
+    xwl_screen->expecting_event--;
+}
+
+static void
+xwl_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value)
+{
+    xwl_gbm_get(data)->capabilities = value;
+}
+
+static const struct wl_drm_listener xwl_drm_listener = {
+    xwl_drm_handle_device,
+    xwl_drm_handle_format,
+    xwl_drm_handle_authenticated,
+    xwl_drm_handle_capabilities
+};
+
+static void
+xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
+                         uint32_t format)
+{
+}
+
+static void
+xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
+                           uint32_t format, uint32_t modifier_hi,
+                           uint32_t modifier_lo)
+{
+   struct xwl_screen *xwl_screen = data;
+    struct xwl_format *xwl_format = NULL;
+    int i;
+
+    for (i = 0; i < xwl_screen->num_formats; i++) {
+        if (xwl_screen->formats[i].format == format) {
+            xwl_format = &xwl_screen->formats[i];
+            break;
+        }
+    }
+
+    if (xwl_format == NULL) {
+       xwl_screen->num_formats++;
+       xwl_screen->formats = realloc(xwl_screen->formats,
+                                     xwl_screen->num_formats * sizeof(*xwl_format));
+       if (!xwl_screen->formats)
+          return;
+       xwl_format = &xwl_screen->formats[xwl_screen->num_formats - 1];
+       xwl_format->format = format;
+       xwl_format->num_modifiers = 0;
+       xwl_format->modifiers = NULL;
+    }
+
+    xwl_format->num_modifiers++;
+    xwl_format->modifiers = realloc(xwl_format->modifiers,
+                                    xwl_format->num_modifiers * sizeof(uint64_t));
+    if (!xwl_format->modifiers)
+       return;
+    xwl_format->modifiers[xwl_format->num_modifiers - 1]  = (uint64_t) modifier_lo;
+    xwl_format->modifiers[xwl_format->num_modifiers - 1] |= (uint64_t) modifier_hi << 32;
+}
+
+static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = {
+    .format   = xwl_dmabuf_handle_format,
+    .modifier = xwl_dmabuf_handle_modifier
+};
+
+Bool
+xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
+                             uint32_t id, uint32_t version)
+{
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    if (version < 2)
+        return FALSE;
+
+    xwl_gbm->drm =
+        wl_registry_bind(xwl_screen->registry, id, &wl_drm_interface, 2);
+    wl_drm_add_listener(xwl_gbm->drm, &xwl_drm_listener, xwl_screen);
+    xwl_screen->expecting_event++;
+
+    return TRUE;
+}
+
+Bool
+xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen,
+                                uint32_t id, uint32_t version)
+{
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    if (version < 3)
+        return FALSE;
+
+    xwl_gbm->dmabuf =
+        wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, 3);
+    zwp_linux_dmabuf_v1_add_listener(xwl_gbm->dmabuf, &xwl_dmabuf_listener, xwl_screen);
+
+    return TRUE;
+}
+
+static void
+xwl_glamor_gbm_init_wl_registry(struct xwl_screen *xwl_screen,
+                                struct wl_registry *wl_registry,
+                                const char *name,
+                                uint32_t id, uint32_t version)
+{
+    if (strcmp(name, "wl_drm") == 0)
+        xwl_screen_set_drm_interface(xwl_screen, id, version);
+    else if (strcmp(name, "zwp_linux_dmabuf_v1") == 0)
+        xwl_screen_set_dmabuf_interface(xwl_screen, id, version);
+}
+
+static Bool
+xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
+{
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    EGLint major, minor;
+    Bool egl_initialized = FALSE;
+    static const EGLint config_attribs_core[] = {
+        EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
+        EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
+        EGL_CONTEXT_MAJOR_VERSION_KHR,
+        GLAMOR_GL_CORE_VER_MAJOR,
+        EGL_CONTEXT_MINOR_VERSION_KHR,
+        GLAMOR_GL_CORE_VER_MINOR,
+        EGL_NONE
+    };
+
+    if (!xwl_gbm->fd_render_node && !xwl_gbm->drm_authenticated) {
+        ErrorF("Failed to get wl_drm, disabling Glamor and DRI3\n");
+	return FALSE;
+    }
+
+    xwl_gbm->gbm = gbm_create_device(xwl_gbm->drm_fd);
+    if (!xwl_gbm->gbm) {
+        ErrorF("couldn't create gbm device\n");
+        goto error;
+    }
+
+    xwl_screen->egl_display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA,
+                                                     xwl_gbm->gbm);
+    if (xwl_screen->egl_display == EGL_NO_DISPLAY) {
+        ErrorF("glamor_egl_get_display() failed\n");
+        goto error;
+    }
+
+    egl_initialized = eglInitialize(xwl_screen->egl_display, &major, &minor);
+    if (!egl_initialized) {
+        ErrorF("eglInitialize() failed\n");
+        goto error;
+    }
+
+    eglBindAPI(EGL_OPENGL_API);
+
+    xwl_screen->egl_context = eglCreateContext(
+        xwl_screen->egl_display, NULL, EGL_NO_CONTEXT, config_attribs_core);
+    if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
+        xwl_screen->egl_context = eglCreateContext(
+            xwl_screen->egl_display, NULL, EGL_NO_CONTEXT, NULL);
+    }
+
+    if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
+        ErrorF("Failed to create EGL context\n");
+        goto error;
+    }
+
+    if (!eglMakeCurrent(xwl_screen->egl_display,
+                        EGL_NO_SURFACE, EGL_NO_SURFACE,
+                        xwl_screen->egl_context)) {
+        ErrorF("Failed to make EGL context current\n");
+        goto error;
+    }
+
+    if (!epoxy_has_gl_extension("GL_OES_EGL_image"))
+        ErrorF("GL_OES_EGL_image not available\n");
+
+    if (epoxy_has_egl_extension(xwl_screen->egl_display,
+                                "EXT_image_dma_buf_import") &&
+        epoxy_has_egl_extension(xwl_screen->egl_display,
+                                "EXT_image_dma_buf_import_modifiers"))
+       xwl_gbm->dmabuf_capable = TRUE;
+
+    return TRUE;
+error:
+    if (xwl_screen->egl_context != EGL_NO_CONTEXT) {
+        eglDestroyContext(xwl_screen->egl_display, xwl_screen->egl_context);
+        xwl_screen->egl_context = EGL_NO_CONTEXT;
+    }
+
+    if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
+        eglTerminate(xwl_screen->egl_display);
+        xwl_screen->egl_display = EGL_NO_DISPLAY;
+    }
+
+    xwl_glamor_gbm_cleanup(xwl_screen);
+    return FALSE;
+}
+
+static Bool
+xwl_glamor_gbm_init_screen(struct xwl_screen *xwl_screen)
+{
+    if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
+        ErrorF("Failed to initialize dri3\n");
+        goto error;
+    }
+
+    if (!dixRegisterPrivateKey(&xwl_auth_state_private_key, PRIVATE_CLIENT,
+                               0)) {
+        ErrorF("Failed to register private key\n");
+        goto error;
+    }
+
+    if (!AddCallback(&ClientStateCallback, xwl_auth_state_client_callback,
+                     NULL)) {
+        ErrorF("Failed to add client state callback\n");
+        goto error;
+    }
+
+    xwl_screen->screen->CreatePixmap = xwl_glamor_gbm_create_pixmap;
+    xwl_screen->screen->DestroyPixmap = xwl_glamor_gbm_destroy_pixmap;
+
+    return TRUE;
+error:
+    xwl_glamor_gbm_cleanup(xwl_screen);
+    return FALSE;
+}
+
+Bool
+xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
+{
+    struct xwl_gbm_private *xwl_gbm;
+
+    if (!dixRegisterPrivateKey(&xwl_gbm_private_key, PRIVATE_SCREEN, 0))
+        return FALSE;
+
+    xwl_gbm = calloc(sizeof(*xwl_gbm), 1);
+    if (!xwl_gbm) {
+        ErrorF("glamor: Not enough memory to setup GBM, disabling\n");
+        return FALSE;
+    }
+
+    dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key,
+                  xwl_gbm);
+
+    xwl_screen->egl_backend.init_wl_registry = xwl_glamor_gbm_init_wl_registry;
+    xwl_screen->egl_backend.init_egl = xwl_glamor_gbm_init_egl;
+    xwl_screen->egl_backend.init_screen = xwl_glamor_gbm_init_screen;
+    xwl_screen->egl_backend.get_wl_buffer_for_pixmap = xwl_glamor_gbm_get_wl_buffer_for_pixmap;
+
+    return TRUE;
+}
diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index 1b9a6b030..7b24ce7e4 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -25,28 +25,11 @@
 
 #include "xwayland.h"
 
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <xf86drm.h>
-#include <drm_fourcc.h>
-
 #define MESA_EGL_NO_X11_HEADERS
-#include <gbm.h>
 #include <glamor_egl.h>
 
 #include <glamor.h>
 #include <glamor_context.h>
-#include <dri3.h>
-#include "drm-client-protocol.h"
-
-static DevPrivateKeyRec xwl_auth_state_private_key;
-
-struct xwl_pixmap {
-    struct wl_buffer *buffer;
-    struct gbm_bo *bo;
-    void *image;
-    unsigned int texture;
-};
 
 static void
 xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
@@ -59,42 +42,6 @@ xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
         FatalError("Failed to make EGL context current\n");
 }
 
-static uint32_t
-wl_drm_format_for_depth(int depth)
-{
-    switch (depth) {
-    case 15:
-        return WL_DRM_FORMAT_XRGB1555;
-    case 16:
-        return WL_DRM_FORMAT_RGB565;
-    case 24:
-        return WL_DRM_FORMAT_XRGB8888;
-    case 30:
-        return WL_DRM_FORMAT_ARGB2101010;
-    default:
-        ErrorF("unexpected depth: %d\n", depth);
-    case 32:
-        return WL_DRM_FORMAT_ARGB8888;
-    }
-}
-
-static uint32_t
-gbm_format_for_depth(int depth)
-{
-    switch (depth) {
-    case 16:
-        return GBM_FORMAT_RGB565;
-    case 24:
-        return GBM_FORMAT_XRGB8888;
-    case 30:
-        return GBM_FORMAT_ARGB2101010;
-    default:
-        ErrorF("unexpected depth: %d\n", depth);
-    case 32:
-        return GBM_FORMAT_ARGB8888;
-    }
-}
-
 void
 glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
 {
@@ -108,53 +55,15 @@ glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
     xwl_screen->glamor_ctx = glamor_ctx;
 }
 
-static PixmapPtr
-xwl_glamor_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo, int depth)
+void
+xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
+                            struct wl_registry *registry,
+                            uint32_t id, const char *interface,
+                            uint32_t version)
 {
-    PixmapPtr pixmap;
-    struct xwl_pixmap *xwl_pixmap;
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-
-    xwl_pixmap = malloc(sizeof *xwl_pixmap);
-    if (xwl_pixmap == NULL)
-        return NULL;
-
-    pixmap = glamor_create_pixmap(screen,
-                                  gbm_bo_get_width(bo),
-                                  gbm_bo_get_height(bo),
-                                  depth,
-                                  GLAMOR_CREATE_PIXMAP_NO_TEXTURE);
-    if (pixmap == NULL) {
-        free(xwl_pixmap);
-        return NULL;
-    }
-
-    if (lastGLContext != xwl_screen->glamor_ctx) {
-        lastGLContext = xwl_screen->glamor_ctx;
-        xwl_glamor_egl_make_current(xwl_screen->glamor_ctx);
-    }
-
-    xwl_pixmap->bo = bo;
-    xwl_pixmap->buffer = NULL;
-    xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
-                                          xwl_screen->egl_context,
-                                          EGL_NATIVE_PIXMAP_KHR,
-                                          xwl_pixmap->bo, NULL);
-
-    glGenTextures(1, &xwl_pixmap->texture);
-    glBindTexture(GL_TEXTURE_2D, xwl_pixmap->texture);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, xwl_pixmap->image);
-    glBindTexture(GL_TEXTURE_2D, 0);
-
-    xwl_pixmap_set_private(pixmap, xwl_pixmap);
-
-    glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture);
-    glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
-
-    return pixmap;
+    if (xwl_screen->egl_backend.init_wl_registry)
+        xwl_screen->egl_backend.init_wl_registry(xwl_screen, registry,
+                                                 interface, id, version);
 }
 
 struct wl_buffer *
@@ -164,133 +73,14 @@ xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
                                 Bool *created)
 {
     struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
-    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
-    int prime_fd;
-    int num_planes;
-    uint32_t strides[4];
-    uint32_t offsets[4];
-    uint64_t modifier;
-    int i;
-
-    if (xwl_pixmap->buffer) {
-        /* Buffer already exists. Return it and inform caller if interested. */
-        if (created)
-            *created = FALSE;
-        return xwl_pixmap->buffer;
-    }
-
-    /* Buffer does not exist yet. Create now and inform caller if interested. */
-    if (created)
-        *created = TRUE;
-
-    if (!xwl_pixmap->bo)
-       return NULL;
-
-    prime_fd = gbm_bo_get_fd(xwl_pixmap->bo);
-    if (prime_fd == -1)
-        return NULL;
-
-#ifdef GBM_BO_WITH_MODIFIERS
-    num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
-    modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
-    for (i = 0; i < num_planes; i++) {
-        strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
-        offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
-    }
-#else
-    num_planes = 1;
-    modifier = DRM_FORMAT_MOD_INVALID;
-    strides[0] = gbm_go_get_stride(xwl_pixmap->bo);
-    offsets[0] = 0;
-#endif
-
-    if (xwl_screen->dmabuf && modifier != DRM_FORMAT_MOD_INVALID) {
-        struct zwp_linux_buffer_params_v1 *params;
-
-        params = zwp_linux_dmabuf_v1_create_params(xwl_screen->dmabuf);
-        for (i = 0; i < num_planes; i++) {
-            zwp_linux_buffer_params_v1_add(params, prime_fd, i,
-                                           offsets[i], strides[i],
-                                           modifier >> 32, modifier & 0xffffffff);
-        }
-
-        xwl_pixmap->buffer =
-           zwp_linux_buffer_params_v1_create_immed(params,
-                                                   width,
-                                                   height,
-                                                   wl_drm_format_for_depth(pixmap->drawable.depth),
-                                                   0);
-        zwp_linux_buffer_params_v1_destroy(params);
-    } else if (num_planes == 1) {
-        xwl_pixmap->buffer =
-            wl_drm_create_prime_buffer(xwl_screen->drm, prime_fd,
-                                       width,
-                                       height,
-                                       wl_drm_format_for_depth(pixmap->drawable.depth),
-                                       0, gbm_bo_get_stride(xwl_pixmap->bo),
-                                       0, 0,
-                                       0, 0);
-    }
-
-    close(prime_fd);
-    return xwl_pixmap->buffer;
-}
-
-static PixmapPtr
-xwl_glamor_create_pixmap(ScreenPtr screen,
-                         int width, int height, int depth, unsigned int hint)
-{
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-    struct gbm_bo *bo;
-    uint32_t format;
-
-    if (width > 0 && height > 0 && depth >= 15 &&
-        (hint == 0 ||
-         hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP ||
-         hint == CREATE_PIXMAP_USAGE_SHARED)) {
-        format = gbm_format_for_depth(depth);
-
-#ifdef GBM_BO_WITH_MODIFIERS
-        if (xwl_screen->dmabuf_capable) {
-            uint32_t num_modifiers;
-            uint64_t *modifiers = NULL;
-
-            glamor_get_modifiers(screen, format, &num_modifiers, &modifiers);
-            bo = gbm_bo_create_with_modifiers(xwl_screen->gbm, width, height,
-                                              format, modifiers, num_modifiers);
-            free(modifiers);
-        }
-        else
-#endif
-        {
-            bo = gbm_bo_create(xwl_screen->gbm, width, height, format,
-                               GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
-        }
-
-        if (bo)
-            return xwl_glamor_create_pixmap_for_bo(screen, bo, depth);
-    }
-
-    return glamor_create_pixmap(screen, width, height, depth, hint);
-}
-
-static Bool
-xwl_glamor_destroy_pixmap(PixmapPtr pixmap)
-{
-    struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
-    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
 
-    if (xwl_pixmap && pixmap->refcnt == 1) {
-        if (xwl_pixmap->buffer)
-            wl_buffer_destroy(xwl_pixmap->buffer);
+    if (xwl_screen->egl_backend.get_wl_buffer_for_pixmap)
+        return xwl_screen->egl_backend.get_wl_buffer_for_pixmap(pixmap,
+                                                                width,
+                                                                height,
+                                                                created);
 
-        eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image);
-        if (xwl_pixmap->bo)
-           gbm_bo_destroy(xwl_pixmap->bo);
-        free(xwl_pixmap);
-    }
-
-    return glamor_destroy_pixmap(pixmap);
+    return NULL;
 }
 
 static Bool
@@ -312,10 +102,9 @@ xwl_glamor_create_screen_resources(ScreenPtr screen)
             fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0);
     }
     else {
-        screen->devPrivate =
-            xwl_glamor_create_pixmap(screen, screen->width, screen->height,
-                                     screen->rootDepth,
-                                     CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
+        screen->devPrivate = screen->CreatePixmap(
+            screen, screen->width, screen->height, screen->rootDepth,
+            CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
     }
 
     SetRootClip(screen, xwl_screen->root_clip_mode);
@@ -323,224 +112,6 @@ xwl_glamor_create_screen_resources(ScreenPtr screen)
     return screen->devPrivate != NULL;
 }
 
-static char
-is_fd_render_node(int fd)
-{
-    struct stat render;
-
-    if (fstat(fd, &render))
-        return 0;
-    if (!S_ISCHR(render.st_mode))
-        return 0;
-    if (render.st_rdev & 0x80)
-        return 1;
-
-    return 0;
-}
-
-static void
-xwl_drm_init_egl(struct xwl_screen *xwl_screen)
-{
-    EGLint major, minor;
-    static const EGLint config_attribs_core[] = {
-        EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
-        EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
-        EGL_CONTEXT_MAJOR_VERSION_KHR,
-        GLAMOR_GL_CORE_VER_MAJOR,
-        EGL_CONTEXT_MINOR_VERSION_KHR,
-        GLAMOR_GL_CORE_VER_MINOR,
-        EGL_NONE
-    };
-
-    if (xwl_screen->egl_display)
-        return;
-
-    xwl_screen->gbm = gbm_create_device(xwl_screen->drm_fd);
-    if (xwl_screen->gbm == NULL) {
-        ErrorF("couldn't get display device\n");
-        return;
-    }
-
-    xwl_screen->egl_display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA,
-                                                     xwl_screen->gbm);
-    if (xwl_screen->egl_display == EGL_NO_DISPLAY) {
-        ErrorF("glamor_egl_get_display() failed\n");
-        return;
-    }
-
-    if (!eglInitialize(xwl_screen->egl_display, &major, &minor)) {
-        ErrorF("eglInitialize() failed\n");
-        return;
-    }
-
-    eglBindAPI(EGL_OPENGL_API);
-
-    xwl_screen->egl_context = eglCreateContext(xwl_screen->egl_display,
-                                               NULL, EGL_NO_CONTEXT, config_attribs_core);
-    if (!xwl_screen->egl_context)
-        xwl_screen->egl_context = eglCreateContext(xwl_screen->egl_display,
-                                                   NULL, EGL_NO_CONTEXT, NULL);
-
-    if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
-        ErrorF("Failed to create EGL context\n");
-        return;
-    }
-
-    if (!eglMakeCurrent(xwl_screen->egl_display,
-                        EGL_NO_SURFACE, EGL_NO_SURFACE,
-                        xwl_screen->egl_context)) {
-        ErrorF("Failed to make EGL context current\n");
-        return;
-    }
-
-    if (!epoxy_has_gl_extension("GL_OES_EGL_image")) {
-        ErrorF("GL_OES_EGL_image not available\n");
-        return;
-    }
-
-    if (epoxy_has_egl_extension(xwl_screen->egl_display,
-                                "EXT_image_dma_buf_import") &&
-        epoxy_has_egl_extension(xwl_screen->egl_display,
-                                "EXT_image_dma_buf_import_modifiers"))
-       xwl_screen->dmabuf_capable = TRUE;
-
-    return;
-}
-
-static void
-xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device)
-{
-   struct xwl_screen *xwl_screen = data;
-   drm_magic_t magic;
-
-   xwl_screen->device_name = strdup(device);
-   if (!xwl_screen->device_name)
-      return;
-
-   xwl_screen->drm_fd = open(xwl_screen->device_name, O_RDWR | O_CLOEXEC);
-   if (xwl_screen->drm_fd == -1) {
-       ErrorF("wayland-egl: could not open %s (%s)\n",
-              xwl_screen->device_name, strerror(errno));
-       return;
-   }
-
-   xwl_screen->expecting_event--;
-
-   if (is_fd_render_node(xwl_screen->drm_fd)) {
-       xwl_screen->fd_render_node = 1;
-   } else {
-       drmGetMagic(xwl_screen->drm_fd, &magic);
-       wl_drm_authenticate(xwl_screen->drm, magic);
-       xwl_screen->expecting_event++; /* wait for 'authenticated' */
-   }
-}
-
-static void
-xwl_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
-{
-}
-
-static void
-xwl_drm_handle_authenticated(void *data, struct wl_drm *drm)
-{
-    struct xwl_screen *xwl_screen = data;
-
-    xwl_screen->drm_authenticated = 1;
-    xwl_screen->expecting_event--;
-}
-
-static void
-xwl_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value)
-{
-   struct xwl_screen *xwl_screen = data;
-
-   xwl_screen->capabilities = value;
-}
-
-static const struct wl_drm_listener xwl_drm_listener = {
-    xwl_drm_handle_device,
-    xwl_drm_handle_format,
-    xwl_drm_handle_authenticated,
-    xwl_drm_handle_capabilities
-};
-
-static void
-xwl_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
-                         uint32_t format)
-{
-}
-
-static void
-xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
-                           uint32_t format, uint32_t modifier_hi,
-                           uint32_t modifier_lo)
-{
-   struct xwl_screen *xwl_screen = data;
-    struct xwl_format *xwl_format = NULL;
-    int i;
-
-    for (i = 0; i < xwl_screen->num_formats; i++) {
-        if (xwl_screen->formats[i].format == format) {
-            xwl_format = &xwl_screen->formats[i];
-            break;
-        }
-    }
-
-    if (xwl_format == NULL) {
-       xwl_screen->num_formats++;
-       xwl_screen->formats = realloc(xwl_screen->formats,
-                                     xwl_screen->num_formats * sizeof(*xwl_format));
-       if (!xwl_screen->formats)
-          return;
-       xwl_format = &xwl_screen->formats[xwl_screen->num_formats - 1];
-       xwl_format->format = format;
-       xwl_format->num_modifiers = 0;
-       xwl_format->modifiers = NULL;
-    }
-
-    xwl_format->num_modifiers++;
-    xwl_format->modifiers = realloc(xwl_format->modifiers,
-                                    xwl_format->num_modifiers * sizeof(uint64_t));
-    if (!xwl_format->modifiers)
-       return;
-    xwl_format->modifiers[xwl_format->num_modifiers - 1]  = (uint64_t) modifier_lo;
-    xwl_format->modifiers[xwl_format->num_modifiers - 1] |= (uint64_t) modifier_hi << 32;
-}
-
-static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = {
-    .format   = xwl_dmabuf_handle_format,
-    .modifier = xwl_dmabuf_handle_modifier
-};
-
-Bool
-xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
-                             uint32_t id, uint32_t version)
-{
-    if (version < 2)
-        return FALSE;
-
-    xwl_screen->drm =
-        wl_registry_bind(xwl_screen->registry, id, &wl_drm_interface, 2);
-    wl_drm_add_listener(xwl_screen->drm, &xwl_drm_listener, xwl_screen);
-    xwl_screen->expecting_event++;
-
-    return TRUE;
-}
-
-Bool
-xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen,
-                                uint32_t id, uint32_t version)
-{
-    if (version < 3)
-        return FALSE;
-
-    xwl_screen->dmabuf =
-        wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, 3);
-    zwp_linux_dmabuf_v1_add_listener(xwl_screen->dmabuf, &xwl_dmabuf_listener, xwl_screen);
-
-    return TRUE;
-}
-
 int
 glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
                                PixmapPtr pixmap,
@@ -549,277 +120,6 @@ glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
     return 0;
 }
 
-struct xwl_auth_state {
-    int fd;
-    ClientPtr client;
-    struct wl_callback *callback;
-};
-
-static void
-free_xwl_auth_state(ClientPtr pClient, struct xwl_auth_state *state)
-{
-    dixSetPrivate(&pClient->devPrivates, &xwl_auth_state_private_key, NULL);
-    if (state) {
-        wl_callback_destroy(state->callback);
-        free(state);
-    }
-}
-
-static void
-xwl_auth_state_client_callback(CallbackListPtr *pcbl, void *unused, void *data)
-{
-    NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
-    ClientPtr pClient = clientinfo->client;
-    struct xwl_auth_state *state;
-
-    switch (pClient->clientState) {
-    case ClientStateGone:
-    case ClientStateRetained:
-        state = dixLookupPrivate(&pClient->devPrivates, &xwl_auth_state_private_key);
-        free_xwl_auth_state(pClient, state);
-        break;
-    default:
-        break;
-    }
-}
-
-static void
-sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
-{
-    struct xwl_auth_state *state = data;
-    ClientPtr client = state->client;
-
-    /* if the client is gone, the callback is cancelled so it's safe to
-     * assume the client is still in ClientStateRunning at this point...
-     */
-    dri3_send_open_reply(client, state->fd);
-    AttendClient(client);
-    free_xwl_auth_state(client, state);
-}
-
-static const struct wl_callback_listener sync_listener = {
-   sync_callback
-};
-
-static int
-xwl_dri3_open_client(ClientPtr client,
-                     ScreenPtr screen,
-                     RRProviderPtr provider,
-                     int *pfd)
-{
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-    struct xwl_auth_state *state;
-    drm_magic_t magic;
-    int fd;
-
-    fd = open(xwl_screen->device_name, O_RDWR | O_CLOEXEC);
-    if (fd < 0)
-        return BadAlloc;
-    if (xwl_screen->fd_render_node) {
-        *pfd = fd;
-        return Success;
-    }
-
-    state = malloc(sizeof *state);
-    if (state == NULL) {
-        close(fd);
-        return BadAlloc;
-    }
-
-    state->client = client;
-    state->fd = fd;
-
-    if (drmGetMagic(state->fd, &magic) < 0) {
-        close(state->fd);
-        free(state);
-        return BadMatch;
-    }
-
-    wl_drm_authenticate(xwl_screen->drm, magic);
-    state->callback = wl_display_sync(xwl_screen->display);
-    wl_callback_add_listener(state->callback, &sync_listener, state);
-    dixSetPrivate(&client->devPrivates, &xwl_auth_state_private_key, state);
-
-    IgnoreClient(client);
-
-    return Success;
-}
-
-_X_EXPORT PixmapPtr
-glamor_pixmap_from_fds(ScreenPtr screen,
-                       CARD8 num_fds, const int *fds,
-                       CARD16 width, CARD16 height,
-                       const CARD32 *strides, const CARD32 *offsets,
-                       CARD8 depth, CARD8 bpp, uint64_t modifier)
-{
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-    struct gbm_bo *bo = NULL;
-    PixmapPtr pixmap;
-    int i;
-
-    if (width == 0 || height == 0 || num_fds == 0 ||
-        depth < 15 || bpp != BitsPerPixel(depth) ||
-        strides[0] < width * bpp / 8)
-       goto error;
-
-    if (xwl_screen->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) {
-#ifdef GBM_BO_WITH_MODIFIERS
-       struct gbm_import_fd_modifier_data data;
-
-       data.width = width;
-       data.height = height;
-       data.num_fds = num_fds;
-       data.format = gbm_format_for_depth(depth);
-       data.modifier = modifier;
-       for (i = 0; i < num_fds; i++) {
-          data.fds[i] = fds[i];
-          data.strides[i] = strides[i];
-          data.offsets[i] = offsets[i];
-       }
-       bo = gbm_bo_import(xwl_screen->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, 0);
-#endif
-    } else if (num_fds == 1) {
-       struct gbm_import_fd_data data;
-
-       data.fd = fds[0];
-       data.width = width;
-       data.height = height;
-       data.stride = strides[0];
-       data.format = gbm_format_for_depth(depth);
-       bo = gbm_bo_import(xwl_screen->gbm, GBM_BO_IMPORT_FD, &data,
-             GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
-    } else {
-       goto error;
-    }
-
-    if (bo == NULL)
-       goto error;
-
-    pixmap = xwl_glamor_create_pixmap_for_bo(screen, bo, depth);
-    if (pixmap == NULL) {
-       gbm_bo_destroy(bo);
-       goto error;
-    }
-
-    return pixmap;
-
-error:
-    return NULL;
-}
-
-_X_EXPORT int
-glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
-                           uint32_t *strides, uint32_t *offsets,
-                           uint64_t *modifier)
-{
-    struct xwl_pixmap *xwl_pixmap;
-#ifdef GBM_BO_WITH_MODIFIERS
-    uint32_t num_fds;
-    int i;
-#endif
-
-    xwl_pixmap = xwl_pixmap_get(pixmap);
-
-    if (!xwl_pixmap->bo)
-       return 0;
-
-#ifdef GBM_BO_WITH_MODIFIERS
-    num_fds = gbm_bo_get_plane_count(xwl_pixmap->bo);
-    *modifier = gbm_bo_get_modifier(xwl_pixmap->bo);
-
-    for (i = 0; i < num_fds; i++) {
-        fds[i] = gbm_bo_get_fd(xwl_pixmap->bo);
-        strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
-        offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
-    }
-
-    return num_fds;
-#else
-    *modifier = DRM_FORMAT_MOD_INVALID;
-    fds[0] = gbm_bo_get_fd(xwl_pixmap->bo);
-    strides[0] = gbm_bo_get_stride(xwl_pixmap->bo);
-    offsets[0] = 0;
-    return 1;
-#endif
-}
-
-_X_EXPORT Bool
-glamor_get_formats(ScreenPtr screen,
-                   CARD32 *num_formats, CARD32 **formats)
-{
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-    int i;
-
-    /* Explicitly zero the count as the caller may ignore the return value */
-    *num_formats = 0;
-
-    if (!xwl_screen->dmabuf_capable || !xwl_screen->dmabuf)
-        return FALSE;
-
-    if (xwl_screen->num_formats == 0)
-       return TRUE;
-
-    *formats = calloc(xwl_screen->num_formats, sizeof(CARD32));
-    if (*formats == NULL)
-        return FALSE;
-
-    for (i = 0; i < xwl_screen->num_formats; i++)
-       (*formats)[i] = xwl_screen->formats[i].format;
-    *num_formats = xwl_screen->num_formats;
-
-    return TRUE;
-}
-
-_X_EXPORT Bool
-glamor_get_modifiers(ScreenPtr screen, CARD32 format,
-                     CARD32 *num_modifiers, uint64_t **modifiers)
-{
-    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
-    struct xwl_format *xwl_format = NULL;
-    int i;
-
-    /* Explicitly zero the count as the caller may ignore the return value */
-    *num_modifiers = 0;
-
-    if (!xwl_screen->dmabuf_capable || !xwl_screen->dmabuf)
-        return FALSE;
-
-    if (xwl_screen->num_formats == 0)
-        return TRUE;
-
-    for (i = 0; i < xwl_screen->num_formats; i++) {
-       if (xwl_screen->formats[i].format == format) {
-          xwl_format = &xwl_screen->formats[i];
-          break;
-       }
-    }
-
-    if (!xwl_format)
-        return FALSE;
-
-    *modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t));
-    if (*modifiers == NULL)
-        return FALSE;
-
-    for (i = 0; i < xwl_format->num_modifiers; i++)
-       (*modifiers)[i] = xwl_format->modifiers[i];
-    *num_modifiers = xwl_format->num_modifiers;
-
-    return TRUE;
-}
-
-
-static const dri3_screen_info_rec xwl_dri3_info = {
-    .version = 2,
-    .open = NULL,
-    .pixmap_from_fds = glamor_pixmap_from_fds,
-    .fds_from_pixmap = glamor_fds_from_pixmap,
-    .open_client = xwl_dri3_open_client,
-    .get_formats = glamor_get_formats,
-    .get_modifiers = glamor_get_modifiers,
-    .get_drawable_modifiers = glamor_get_drawable_modifiers,
-};
-
 Bool
 xwl_glamor_init(struct xwl_screen *xwl_screen)
 {
@@ -832,14 +132,8 @@ xwl_glamor_init(struct xwl_screen *xwl_screen)
         return FALSE;
     }
 
-    if (!xwl_screen->fd_render_node && !xwl_screen->drm_authenticated) {
-        ErrorF("Failed to get wl_drm, disabling Glamor and DRI3\n");
-	return FALSE;
-    }
-
-    xwl_drm_init_egl(xwl_screen);
-    if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
-        ErrorF("Disabling glamor and dri3, EGL setup failed\n");
+    if (!xwl_screen->egl_backend.init_egl(xwl_screen)) {
+        ErrorF("EGL setup failed, disabling glamor\n");
         return FALSE;
     }
 
@@ -848,25 +142,13 @@ xwl_glamor_init(struct xwl_screen *xwl_screen)
         return FALSE;
     }
 
-    if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
-        ErrorF("Failed to initialize dri3\n");
-        return FALSE;
-    }
-
-    if (!dixRegisterPrivateKey(&xwl_auth_state_private_key, PRIVATE_CLIENT, 0)) {
-        ErrorF("Failed to register private key\n");
-        return FALSE;
-    }
-
-    if (!AddCallback(&ClientStateCallback, xwl_auth_state_client_callback, NULL)) {
-        ErrorF("Failed to add client state callback\n");
+    if (!xwl_screen->egl_backend.init_screen(xwl_screen)) {
+        ErrorF("EGL backend init_screen() failed, disabling glamor\n");
         return FALSE;
     }
 
     xwl_screen->CreateScreenResources = screen->CreateScreenResources;
     screen->CreateScreenResources = xwl_glamor_create_screen_resources;
-    screen->CreatePixmap = xwl_glamor_create_pixmap;
-    screen->DestroyPixmap = xwl_glamor_destroy_pixmap;
 
 #ifdef XV
     if (!xwl_glamor_xv_init(screen))
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index 72493285e..b37b62c78 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -755,13 +755,9 @@ registry_global(void *data, struct wl_registry *registry, uint32_t id,
         xwl_screen_init_xdg_output(xwl_screen);
     }
 #ifdef GLAMOR_HAS_GBM
-    else if (xwl_screen->glamor &&
-             strcmp(interface, "wl_drm") == 0 && version >= 2) {
-        xwl_screen_set_drm_interface(xwl_screen, id, version);
-    }
-    else if (xwl_screen->glamor &&
-             strcmp(interface, "zwp_linux_dmabuf_v1") == 0 && version >= 3) {
-        xwl_screen_set_dmabuf_interface(xwl_screen, id, version);
+    else if (xwl_screen->glamor) {
+        xwl_glamor_init_wl_registry(xwl_screen, registry, id, interface,
+                                    version);
     }
 #endif
 }
@@ -970,6 +966,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
         }
     }
 
+    if (xwl_screen->glamor) {
+        if (!xwl_glamor_init_gbm(xwl_screen)) {
+            ErrorF("xwayland glamor: failed to setup GBM backend, falling back to sw accel\n");
+            xwl_screen->glamor = 0;
+        }
+    }
+
     /* In rootless mode, we don't have any screen storage, and the only
      * rendering should be to redirected mode. */
     if (xwl_screen->rootless)
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index 11a9f4816..82cbfc9d3 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -55,6 +55,9 @@ struct xwl_format {
     uint64_t *modifiers;
 };
 
+struct xwl_pixmap;
+struct xwl_window;
+
 struct xwl_screen {
     int width;
     int height;
@@ -101,19 +104,46 @@ struct xwl_screen {
     int prepare_read;
     int wait_flush;
 
-    char *device_name;
-    int drm_fd;
-    int fd_render_node;
-    int drm_authenticated;
-    struct wl_drm *drm;
-    struct zwp_linux_dmabuf_v1 *dmabuf;
     uint32_t num_formats;
     struct xwl_format *formats;
-    uint32_t capabilities;
     void *egl_display, *egl_context;
-    struct gbm_device *gbm;
+
+    /* the current backend for creating pixmaps on wayland */
+    struct {
+        /* Called once for each interface in the global registry. Backends
+         * should use this to bind to any wayland interfaces they need. This
+         * callback is optional.
+         */
+        void (*init_wl_registry)(struct xwl_screen *xwl_screen,
+                                 struct wl_registry *wl_registry,
+                                 const char *name, uint32_t id,
+                                 uint32_t version);
+
+        /* Called before glamor has been initialized. Backends should setup a
+         * valid, glamor compatible EGL context in this hook.
+         */
+        Bool (*init_egl)(struct xwl_screen *xwl_screen);
+
+        /* Called after glamor has been initialized, and after all of the
+         * common Xwayland DDX hooks have been connected. Backends should use
+         * this to setup any required wraps around X server callbacks like
+         * CreatePixmap.
+         */
+        Bool (*init_screen)(struct xwl_screen *xwl_screen);
+
+        /* Called by Xwayland to retrieve a pointer to a valid wl_buffer for
+         * the given window/pixmap combo so that damage to the pixmap may be
+         * displayed on-screen. Backends should use this to create a new
+         * wl_buffer for a currently buffer-less pixmap, or simply return the
+         * pixmap they've prepared beforehand.
+         */
+        struct wl_buffer *(*get_wl_buffer_for_pixmap)(PixmapPtr pixmap,
+                                                      unsigned short width,
+                                                      unsigned short height,
+                                                      Bool *created);
+    } egl_backend;
+
     struct glamor_context *glamor_ctx;
-    int dmabuf_capable;
 
     Atom allow_commits_prop;
 };
@@ -318,8 +348,6 @@ struct xwl_output {
     Bool xdg_output_done;
 };
 
-struct xwl_pixmap;
-
 void xwl_sync_events (struct xwl_screen *xwl_screen);
 
 Bool xwl_screen_init_cursor(struct xwl_screen *xwl_screen);
@@ -380,6 +408,10 @@ struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
                                                   unsigned short width,
                                                   unsigned short height,
                                                   Bool *created);
+void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
+                                 struct wl_registry *registry,
+                                 uint32_t id, const char *interface,
+                                 uint32_t version);
 
 #ifdef GLAMOR_HAS_GBM
 Bool xwl_present_init(ScreenPtr screen);
@@ -400,4 +432,8 @@ Bool xwl_glamor_xv_init(ScreenPtr pScreen);
 void xwlVidModeExtensionInit(void);
 #endif
 
+#ifdef GLAMOR_HAS_GBM
+Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen);
+#endif
+
 #endif


More information about the xorg-commit mailing list