[Mesa-dev] [PATCH RFC] egl/dri2: Add dri3 support to x11 platform

Boyan Ding boyan.j.ding at gmail.com
Wed Jul 1 08:31:22 PDT 2015


Signed-off-by: Boyan Ding <boyan.j.ding at gmail.com>
---
 configure.ac                             |    3 +
 src/egl/drivers/dri2/Makefile.am         |    5 +
 src/egl/drivers/dri2/egl_dri2.c          |   65 +-
 src/egl/drivers/dri2/egl_dri2.h          |   12 +-
 src/egl/drivers/dri2/platform_x11.c      |  127 ++-
 src/egl/drivers/dri2/platform_x11_dri3.c | 1591 ++++++++++++++++++++++++++++++
 src/egl/drivers/dri2/platform_x11_dri3.h |  140 +++
 7 files changed, 1926 insertions(+), 17 deletions(-)
 create mode 100644 src/egl/drivers/dri2/platform_x11_dri3.c
 create mode 100644 src/egl/drivers/dri2/platform_x11_dri3.h

diff --git a/configure.ac b/configure.ac
index af61aa2..090e6c9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1545,6 +1545,9 @@ if test "x$enable_egl" = xyes; then
             if test "x$enable_shared_glapi" = xno; then
                 AC_MSG_ERROR([egl_dri2 requires --enable-shared-glapi])
             fi
+            if test "x$enable_dri3" = xyes; then
+                EGL_LIB_DEPS="$EGL_LIB_DEPS -lxcb-dri3 -lxcb-present -lXxf86vm -lxshmfence -lxcb-sync"
+            fi
         else
             # Avoid building an "empty" libEGL. Drop/update this
             # when other backends (haiku?) come along.
diff --git a/src/egl/drivers/dri2/Makefile.am b/src/egl/drivers/dri2/Makefile.am
index 55be4a7..d5b511c 100644
--- a/src/egl/drivers/dri2/Makefile.am
+++ b/src/egl/drivers/dri2/Makefile.am
@@ -52,6 +52,11 @@ if HAVE_EGL_PLATFORM_X11
 libegl_dri2_la_SOURCES += platform_x11.c
 AM_CFLAGS += -DHAVE_X11_PLATFORM
 AM_CFLAGS += $(XCB_DRI2_CFLAGS)
+if HAVE_DRI3
+libegl_dri2_la_SOURCES += \
+	platform_x11_dri3.c \
+	platform_x11_dri3.h
+endif
 endif
 
 if HAVE_EGL_PLATFORM_WAYLAND
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index b1b65f7..052c579 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -322,6 +322,12 @@ struct dri2_extension_match {
    int offset;
 };
 
+static struct dri2_extension_match dri3_driver_extensions[] = {
+   { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
+   { __DRI_IMAGE_DRIVER, 1, offsetof(struct dri2_egl_display, image_driver) },
+   { NULL, 0, 0 }
+};
+
 static struct dri2_extension_match dri2_driver_extensions[] = {
    { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
    { __DRI_DRI2, 2, offsetof(struct dri2_egl_display, dri2) },
@@ -464,6 +470,24 @@ dri2_open_driver(_EGLDisplay *disp)
 }
 
 EGLBoolean
+dri2_load_driver_dri3(_EGLDisplay *disp)
+{
+   struct dri2_egl_display *dri2_dpy = disp->DriverData;
+   const __DRIextension **extensions;
+
+   extensions = dri2_open_driver(disp);
+   if (!extensions)
+      return EGL_FALSE;
+
+   if (!dri2_bind_extensions(dri2_dpy, dri3_driver_extensions, extensions)) {
+      dlclose(dri2_dpy->driver);
+   }
+   dri2_dpy->driver_extensions = extensions;
+
+   return EGL_TRUE;
+}
+
+EGLBoolean
 dri2_load_driver(_EGLDisplay *disp)
 {
    struct dri2_egl_display *dri2_dpy = disp->DriverData;
@@ -507,7 +531,9 @@ dri2_setup_screen(_EGLDisplay *disp)
    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
    unsigned int api_mask;
 
-   if (dri2_dpy->dri2) {
+   if (dri2_dpy->image_driver) {
+      api_mask = dri2_dpy->image_driver->getAPIMask(dri2_dpy->dri_screen);
+   } else if (dri2_dpy->dri2) {
       api_mask = dri2_dpy->dri2->getAPIMask(dri2_dpy->dri_screen);
    } else {
       assert(dri2_dpy->swrast);
@@ -527,11 +553,12 @@ dri2_setup_screen(_EGLDisplay *disp)
    if (api_mask & (1 << __DRI_API_GLES3))
       disp->ClientAPIs |= EGL_OPENGL_ES3_BIT_KHR;
 
-   assert(dri2_dpy->dri2 || dri2_dpy->swrast);
+   assert(dri2_dpy->image_driver || dri2_dpy->dri2 || dri2_dpy->swrast);
    disp->Extensions.KHR_surfaceless_context = EGL_TRUE;
    disp->Extensions.MESA_configless_context = EGL_TRUE;
 
-   if ((dri2_dpy->dri2 && dri2_dpy->dri2->base.version >= 3) ||
+   if (dri2_dpy->image_driver ||
+       (dri2_dpy->dri2 && dri2_dpy->dri2->base.version >= 3) ||
        (dri2_dpy->swrast && dri2_dpy->swrast->base.version >= 3)) {
       disp->Extensions.KHR_create_context = EGL_TRUE;
 
@@ -591,7 +618,14 @@ dri2_create_screen(_EGLDisplay *disp)
 
    dri2_dpy = disp->DriverData;
 
-   if (dri2_dpy->dri2) {
+   if (dri2_dpy->image_driver) {
+      dri2_dpy->dri_screen =
+         dri2_dpy->image_driver->createNewScreen2(0, dri2_dpy->fd,
+                                                  dri2_dpy->extensions,
+                                                  dri2_dpy->driver_extensions,
+                                                  &dri2_dpy->driver_configs,
+                                                  disp);
+   } else if (dri2_dpy->dri2) {
       if (dri2_dpy->dri2->base.version >= 4) {
          dri2_dpy->dri_screen =
             dri2_dpy->dri2->createNewScreen2(0, dri2_dpy->fd,
@@ -627,7 +661,7 @@ dri2_create_screen(_EGLDisplay *disp)
 
    extensions = dri2_dpy->core->getExtensions(dri2_dpy->dri_screen);
 
-   if (dri2_dpy->dri2) {
+   if (dri2_dpy->image_driver || dri2_dpy->dri2) {
       if (!dri2_bind_extensions(dri2_dpy, dri2_core_extensions, extensions))
          goto cleanup_dri_screen;
    } else {
@@ -971,7 +1005,26 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf,
    else
       dri_config = NULL;
 
-   if (dri2_dpy->dri2) {
+   if (dri2_dpy->image_driver) {
+      unsigned error;
+      unsigned num_attribs = 8;
+      uint32_t ctx_attribs[8];
+
+      if (!dri2_fill_context_attribs(dri2_ctx, dri2_dpy, ctx_attribs,
+                                        &num_attribs))
+         goto cleanup;
+
+      dri2_ctx->dri_context =
+         dri2_dpy->image_driver->createContextAttribs(dri2_dpy->dri_screen,
+                                                      api,
+                                                      dri_config,
+                                                      shared,
+                                                      num_attribs / 2,
+                                                      ctx_attribs,
+                                                      & error,
+                                                      dri2_ctx);
+      dri2_create_context_attribs_error(error);
+   } else if (dri2_dpy->dri2) {
       if (dri2_dpy->dri2->base.version >= 3) {
          unsigned error;
          unsigned num_attribs = 8;
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index f0cc6da..d258753 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -153,11 +153,16 @@ struct dri2_egl_display
 
    int                       dri2_major;
    int                       dri2_minor;
+   int                       dri3_major;
+   int                       dri3_minor;
+   int                       present_major;
+   int                       present_minor;
    __DRIscreen              *dri_screen;
    int                       own_dri_screen;
    const __DRIconfig       **driver_configs;
    void                     *driver;
    const __DRIcoreExtension       *core;
+   const __DRIimageDriverExtension *image_driver;
    const __DRIdri2Extension       *dri2;
    const __DRIswrastExtension     *swrast;
    const __DRI2flushExtension     *flush;
@@ -189,6 +194,7 @@ struct dri2_egl_display
 #ifdef HAVE_X11_PLATFORM
    xcb_connection_t         *conn;
    int                      screen;
+   Display                  *dpy;
 #endif
 
 #ifdef HAVE_WAYLAND_PLATFORM
@@ -202,8 +208,9 @@ struct dri2_egl_display
    int			     formats;
    uint32_t                  capabilities;
    int			     is_render_node;
-   int			     is_different_gpu;
 #endif
+
+   int			     is_different_gpu;
 };
 
 struct dri2_egl_context
@@ -324,6 +331,9 @@ EGLBoolean
 dri2_load_driver_swrast(_EGLDisplay *disp);
 
 EGLBoolean
+dri2_load_driver_dri3(_EGLDisplay *disp);
+
+EGLBoolean
 dri2_create_screen(_EGLDisplay *disp);
 
 __DRIimage *
diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c
index ad40bd5..2ce260f 100644
--- a/src/egl/drivers/dri2/platform_x11.c
+++ b/src/egl/drivers/dri2/platform_x11.c
@@ -45,6 +45,10 @@
 #include "egl_dri2_fallbacks.h"
 #include "loader.h"
 
+#ifdef HAVE_DRI3
+#include "platform_x11_dri3.h"
+#endif
+
 static EGLBoolean
 dri2_x11_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
                        EGLint interval);
@@ -1094,13 +1098,17 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp)
 
    disp->DriverData = (void *) dri2_dpy;
    if (disp->PlatformDisplay == NULL) {
-      dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen);
+      dri2_dpy->dpy = XOpenDisplay(NULL);
+
+      dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+      dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+
       dri2_dpy->own_device = true;
    } else {
-      Display *dpy = disp->PlatformDisplay;
+      dri2_dpy->dpy = disp->PlatformDisplay;
 
-      dri2_dpy->conn = XGetXCBConnection(dpy);
-      dri2_dpy->screen = DefaultScreen(dpy);
+      dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+      dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
    }
 
    if (xcb_connection_has_error(dri2_dpy->conn)) {
@@ -1202,6 +1210,94 @@ dri2_x11_setup_swap_interval(struct dri2_egl_display *dri2_dpy)
    }
 }
 
+#ifdef HAVE_DRI3
+static EGLBoolean
+dri2_initialize_x11_dri3(_EGLDriver *drv, _EGLDisplay *disp)
+{
+   struct dri2_egl_display *dri2_dpy;
+
+   dri2_dpy = calloc(1, sizeof *dri2_dpy);
+   if (!dri2_dpy)
+      return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+
+   disp->DriverData = (void *) dri2_dpy;
+   if (disp->PlatformDisplay == NULL) {
+      dri2_dpy->dpy = XOpenDisplay(NULL);
+
+      dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+      dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+
+      dri2_dpy->own_device = true;
+   } else {
+      dri2_dpy->dpy = disp->PlatformDisplay;
+
+      dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+      dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+   }
+
+   if (xcb_connection_has_error(dri2_dpy->conn)) {
+      _eglLog(_EGL_WARNING, "DRI2: xcb_connect failed");
+      goto cleanup_dpy;
+   }
+
+   if (dri2_dpy->conn) {
+      if (!dri3_x11_connect(dri2_dpy))
+	 goto cleanup_conn;
+   }
+
+   if (!dri2_load_driver_dri3(disp))
+      goto cleanup_conn;
+
+   dri2_dpy->extensions[0] = &dri3_image_loader_extension.base;
+   dri2_dpy->extensions[1] = &dri3_system_time_extension.base;
+   dri2_dpy->extensions[2] = &use_invalidate.base;
+   dri2_dpy->extensions[3] = &image_lookup_extension.base;
+   dri2_dpy->extensions[4] = NULL;
+
+   dri2_dpy->swap_available = true;
+   dri2_dpy->invalidate_available = true;
+
+   if (!dri2_create_screen(disp))
+      goto cleanup_fd;
+
+   dri2_x11_setup_swap_interval(dri2_dpy);
+
+   disp->Extensions.NOK_texture_from_pixmap = EGL_TRUE;
+   disp->Extensions.CHROMIUM_sync_control = EGL_TRUE;
+   disp->Extensions.EXT_buffer_age = EGL_TRUE;
+
+#ifdef HAVE_WAYLAND_PLATFORM
+   disp->Extensions.WL_bind_wayland_display = EGL_TRUE;
+#endif
+
+   if (dri2_dpy->conn) {
+      if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp))
+	 goto cleanup_configs;
+   }
+
+   /* Fill vtbl last to prevent accidentally calling virtual function during
+    * initialization.
+    */
+   dri2_dpy->vtbl = &dri3_x11_display_vtbl;
+
+   return EGL_TRUE;
+
+ cleanup_configs:
+   _eglCleanupDisplay(disp);
+   dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
+   dlclose(dri2_dpy->driver);
+ cleanup_fd:
+   close(dri2_dpy->fd);
+ cleanup_conn:
+   if (disp->PlatformDisplay == NULL)
+      xcb_disconnect(dri2_dpy->conn);
+ cleanup_dpy:
+   free(dri2_dpy);
+
+   return EGL_FALSE;
+}
+#endif
+
 static EGLBoolean
 dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp)
 {
@@ -1213,13 +1309,17 @@ dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp)
 
    disp->DriverData = (void *) dri2_dpy;
    if (disp->PlatformDisplay == NULL) {
-      dri2_dpy->conn = xcb_connect(0, &dri2_dpy->screen);
+      dri2_dpy->dpy = XOpenDisplay(NULL);
+
+      dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+      dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
+
       dri2_dpy->own_device = true;
    } else {
-      Display *dpy = disp->PlatformDisplay;
+      dri2_dpy->dpy = disp->PlatformDisplay;
 
-      dri2_dpy->conn = XGetXCBConnection(dpy);
-      dri2_dpy->screen = DefaultScreen(dpy);
+      dri2_dpy->conn = XGetXCBConnection(dri2_dpy->dpy);
+      dri2_dpy->screen = DefaultScreen(dri2_dpy->dpy);
    }
 
    if (xcb_connection_has_error(dri2_dpy->conn)) {
@@ -1321,9 +1421,16 @@ dri2_initialize_x11(_EGLDriver *drv, _EGLDisplay *disp)
    int x11_dri2_accel = (getenv("LIBGL_ALWAYS_SOFTWARE") == NULL);
 
    if (x11_dri2_accel) {
-      if (!dri2_initialize_x11_dri2(drv, disp)) {
-         initialized = dri2_initialize_x11_swrast(drv, disp);
+#ifdef HAVE_DRI3
+      if (getenv("LIBGL_DRI3_ENABLE") == NULL ||
+          !dri2_initialize_x11_dri3(drv, disp)) {
+#endif
+         if (!dri2_initialize_x11_dri2(drv, disp)) {
+            initialized = dri2_initialize_x11_swrast(drv, disp);
+         }
+#ifdef HAVE_DRI3
       }
+#endif
    } else {
       initialized = dri2_initialize_x11_swrast(drv, disp);
    }
diff --git a/src/egl/drivers/dri2/platform_x11_dri3.c b/src/egl/drivers/dri2/platform_x11_dri3.c
new file mode 100644
index 0000000..dcd7128
--- /dev/null
+++ b/src/egl/drivers/dri2/platform_x11_dri3.c
@@ -0,0 +1,1591 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef XF86VIDMODE
+#include <X11/extensions/xf86vmode.h>
+#endif
+#include <X11/xshmfence.h>
+#include <xcb/xcb.h>
+#include <xcb/dri3.h>
+#include <xcb/present.h>
+#include <xf86drm.h>
+
+#include "egl_dri2.h"
+#include "egl_dri2_fallbacks.h"
+#include "platform_x11_dri3.h"
+
+#include "loader.h"
+
+static inline void
+dri3_fence_reset(xcb_connection_t *c, struct dri3_buffer *buffer)
+{
+   xshmfence_reset(buffer->shm_fence);
+}
+
+static inline void
+dri3_fence_set(struct dri3_buffer *buffer)
+{
+   xshmfence_trigger(buffer->shm_fence);
+}
+
+static inline void
+dri3_fence_trigger(xcb_connection_t *c, struct dri3_buffer *buffer)
+{
+   xcb_sync_trigger_fence(c, buffer->sync_fence);
+}
+
+static inline void
+dri3_fence_await(xcb_connection_t *c, struct dri3_buffer *buffer)
+{
+   xcb_flush(c);
+   xshmfence_await(buffer->shm_fence);
+}
+
+static void
+dri3_update_num_back(struct dri3_egl_surface *dri3_surf)
+{
+   dri3_surf->num_back = 1;
+   if (dri3_surf->flipping) {
+      if (!dri3_surf->is_pixmap && !(dri3_surf->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC))
+         dri3_surf->num_back++;
+      dri3_surf->num_back++;
+   }
+   if (dri3_surf->base.SwapInterval == 0)
+      dri3_surf->num_back++;
+}
+
+/** dri3_free_render_buffer
+ *
+ * Free everything associated with one render buffer including pixmap, fence
+ * stuff and the driver image
+ */
+static void
+dri3_free_render_buffer(struct dri2_egl_display *dri2_dpy,
+                        struct dri3_egl_surface *dri3_surf,
+                        struct dri3_buffer *buffer)
+{
+   if (buffer->own_pixmap)
+      xcb_free_pixmap(dri2_dpy->conn, buffer->pixmap);
+   xcb_sync_destroy_fence(dri2_dpy->conn, buffer->sync_fence);
+   xshmfence_unmap_shm(buffer->shm_fence);
+   (*dri2_dpy->image->destroyImage)(buffer->image);
+   if (buffer->linear_buffer)
+      (*dri2_dpy->image->destroyImage)(buffer->linear_buffer);
+   free(buffer);
+}
+
+static EGLBoolean
+dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+   int i;
+
+   (void) drv;
+
+   if (!_eglPutSurface(surf))
+      return EGL_TRUE;
+
+   (*dri2_dpy->core->destroyDrawable) (dri3_surf->dri_drawable);
+
+   for (i = 0; i < DRI3_NUM_BUFFERS; i++) {
+      if (dri3_surf->buffers[i])
+         dri3_free_render_buffer(dri2_dpy, dri3_surf, dri3_surf->buffers[i]);
+   }
+
+   if (dri3_surf->special_event)
+      xcb_unregister_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+   free(surf);
+
+   return EGL_TRUE;
+}
+
+static EGLBoolean
+dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
+                       EGLint interval)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+
+   if (interval > surf->Config->MaxSwapInterval)
+      interval = surf->Config->MaxSwapInterval;
+   else if (interval < surf->Config->MinSwapInterval)
+      interval = surf->Config->MinSwapInterval;
+
+   dri3_surf->base.SwapInterval = interval;
+   dri3_update_num_back(dri3_surf);
+
+   return EGL_TRUE;
+}
+
+static xcb_screen_t *
+get_xcb_screen(xcb_screen_iterator_t iter, int screen)
+{
+    for (; iter.rem; --screen, xcb_screen_next(&iter))
+        if (screen == 0)
+            return iter.data;
+
+    return NULL;
+}
+
+/* xDrawable in glx maps to drawable here */
+static _EGLSurface *
+dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
+                    _EGLConfig *conf, void *native_surface,
+                    const EGLint *attrib_list)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
+   struct dri3_egl_surface *dri3_surf;
+   xcb_drawable_t drawable;
+   xcb_get_geometry_cookie_t cookie;
+   xcb_get_geometry_reply_t *reply;
+   xcb_screen_iterator_t s;
+   xcb_generic_error_t *error;
+   xcb_screen_t *screen;
+   GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
+
+   STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
+   drawable = (uintptr_t) native_surface;
+
+   (void) drv;
+
+   dri3_surf = calloc(1, sizeof *dri3_surf);
+   if (!dri3_surf) {
+      _eglError(EGL_BAD_ALLOC, "dri3_create_surface");
+      return NULL;
+   }
+
+   if (!_eglInitSurface(&dri3_surf->base, disp, type, conf, attrib_list))
+      goto cleanup_surf;
+
+   if (type == EGL_PBUFFER_BIT) {
+      s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn));
+      screen = get_xcb_screen(s, dri2_dpy->screen);
+      if (!screen) {
+         _eglError(EGL_BAD_NATIVE_WINDOW, "dri3_create_surface");
+         goto cleanup_surf;
+      }
+
+      dri3_surf->drawable = xcb_generate_id(dri2_dpy->conn);
+      xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
+                        dri3_surf->drawable, screen->root,
+                        dri3_surf->base.Width, dri3_surf->base.Height);
+   } else {
+      dri3_surf->drawable = drawable;
+   }
+
+   dri3_surf->base.SwapInterval = 1; /* default may be overridden below */
+   dri3_surf->have_back = 0;
+   dri3_surf->have_fake_front = 0;
+   dri3_surf->first_init = true;
+
+   if (dri2_dpy->config)
+      dri2_dpy->config->configQueryi(dri2_dpy->dri_screen,
+                                "vblank_mode", &vblank_mode);
+
+   switch (vblank_mode) {
+   case DRI_CONF_VBLANK_NEVER:
+   case DRI_CONF_VBLANK_DEF_INTERVAL_0:
+      dri3_surf->base.SwapInterval = 0;
+      break;
+   case DRI_CONF_VBLANK_DEF_INTERVAL_1:
+   case DRI_CONF_VBLANK_ALWAYS_SYNC:
+   default:
+      dri3_surf->base.SwapInterval = 1;
+      break;
+   }
+
+   dri3_update_num_back(dri3_surf);
+
+   /* Create a new drawable */
+   dri3_surf->dri_drawable =
+      (*dri2_dpy->image_driver->createNewDrawable) (dri2_dpy->dri_screen,
+                                                    type == EGL_WINDOW_BIT ?
+                                                    dri2_conf->dri_double_config :
+                                                    dri2_conf->dri_single_config,
+                                                    dri3_surf);
+
+   if (!dri3_surf->dri_drawable) {
+      _eglError(EGL_BAD_ALLOC, "dri3->createNewDrawable");
+      goto cleanup_pixmap;
+   }
+
+   if (type != EGL_PBUFFER_BIT) {
+      cookie = xcb_get_geometry (dri2_dpy->conn, dri3_surf->drawable);
+      reply = xcb_get_geometry_reply (dri2_dpy->conn, cookie, &error);
+      if (reply == NULL || error != NULL) {
+	 _eglError(EGL_BAD_ALLOC, "xcb_get_geometry");
+	 free(error);
+	 goto cleanup_dri_drawable;
+      }
+
+      dri3_surf->base.Width = reply->width;
+      dri3_surf->base.Height = reply->height;
+      dri3_surf->depth = reply->depth;
+      free(reply);
+   }
+
+   /*
+    * Make sure server has the same swap interval we do for the new
+    * drawable.
+    */
+   dri3_set_swap_interval(drv, disp, &dri3_surf->base,
+                          dri3_surf->base.SwapInterval);
+
+   return &dri3_surf->base;
+
+ cleanup_dri_drawable:
+   dri2_dpy->core->destroyDrawable(dri3_surf->dri_drawable);
+ cleanup_pixmap:
+   if (type == EGL_PBUFFER_BIT)
+      xcb_free_pixmap(dri2_dpy->conn, dri3_surf->drawable);
+ cleanup_surf:
+   free(dri3_surf);
+
+   return NULL;
+}
+
+/**
+ * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
+ */
+static _EGLSurface *
+dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
+                           _EGLConfig *conf, void *native_window,
+                           const EGLint *attrib_list)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   _EGLSurface *surf;
+
+   surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
+                              native_window, attrib_list);
+   if (surf != NULL)
+      dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval);
+
+   return surf;
+}
+
+static _EGLSurface *
+dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
+                           _EGLConfig *conf, void *native_pixmap,
+                           const EGLint *attrib_list)
+{
+   return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
+                              native_pixmap, attrib_list);
+}
+
+static _EGLSurface *
+dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp,
+                                _EGLConfig *conf, const EGLint *attrib_list)
+{
+   return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf,
+                              XCB_WINDOW_NONE, attrib_list);
+}
+
+/*
+ * Process one Present event
+ */
+static void
+dri3_handle_present_event(struct dri2_egl_display *dri2_dpy,
+                          struct dri3_egl_surface *dri3_surf,
+                          xcb_present_generic_event_t *ge)
+{
+   switch (ge->evtype) {
+   case XCB_PRESENT_CONFIGURE_NOTIFY: {
+      xcb_present_configure_notify_event_t *ce = (void *) ge;
+
+      dri3_surf->base.Width = ce->width;
+      dri3_surf->base.Height = ce->height;
+      break;
+   }
+   case XCB_PRESENT_COMPLETE_NOTIFY: {
+      xcb_present_complete_notify_event_t *ce = (void *) ge;
+
+      /* Compute the processed SBC number from the received 32-bit serial number merged
+       * with the upper 32-bits of the sent 64-bit serial number while checking for
+       * wrap
+       */
+      if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
+         dri3_surf->recv_sbc = (dri3_surf->send_sbc & 0xffffffff00000000LL) | ce->serial;
+         if (dri3_surf->recv_sbc > dri3_surf->send_sbc)
+            dri3_surf->recv_sbc -= 0x100000000;
+         switch (ce->mode) {
+         case XCB_PRESENT_COMPLETE_MODE_FLIP:
+            dri3_surf->flipping = true;
+            break;
+         case XCB_PRESENT_COMPLETE_MODE_COPY:
+            dri3_surf->flipping = false;
+            break;
+         }
+         dri3_update_num_back(dri3_surf);
+
+         /*
+         if (psc->show_fps_interval)
+            show_fps(priv, ce->ust); */
+
+         dri3_surf->ust = ce->ust;
+         dri3_surf->msc = ce->msc;
+      } else {
+         dri3_surf->recv_msc_serial = ce->serial;
+         dri3_surf->notify_ust = ce->ust;
+         dri3_surf->notify_msc = ce->msc;
+      }
+      break;
+   }
+   case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
+      xcb_present_idle_notify_event_t *ie = (void *) ge;
+      int b;
+
+      for (b = 0; b < sizeof (dri3_surf->buffers) / sizeof (dri3_surf->buffers[0]); b++) {
+         struct dri3_buffer *buf = dri3_surf->buffers[b];
+
+         if (buf && buf->pixmap == ie->pixmap) {
+            buf->busy = 0;
+            if (dri3_surf->num_back <= b && b < DRI3_MAX_BACK) {
+               dri3_free_render_buffer(dri2_dpy, dri3_surf, buf);
+               dri3_surf->buffers[b] = NULL;
+            }
+            break;
+         }
+      }
+      break;
+   }
+   }
+   free(ge);
+}
+
+static bool
+dri3_wait_for_event(struct dri2_egl_display *dri2_dpy,
+                    struct dri3_egl_surface *dri3_surf)
+{
+   xcb_generic_event_t *ev;
+   xcb_present_generic_event_t *ge;
+
+   xcb_flush(dri2_dpy->conn);
+   ev = xcb_wait_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+   if (!ev)
+      return false;
+   ge = (void *) ev;
+   dri3_handle_present_event(dri2_dpy, dri3_surf, ge);
+   return true;
+}
+
+/** dri3_wait_for_msc
+ *
+ * Get the X server to send an event when the target msc/divisor/remainder is
+ * reached.
+ */
+static EGLBoolean
+dri3_wait_for_msc(struct dri2_egl_display *dri2_dpy,
+                  struct dri3_egl_surface *dri3_surf,
+                  int64_t target_msc, int64_t divisor, int64_t remainder,
+                  int64_t *ust, int64_t *msc, int64_t *sbc)
+{
+   uint32_t msc_serial;
+
+   msc_serial = ++dri3_surf->send_msc_serial;
+   xcb_present_notify_msc(dri2_dpy->conn,
+                          dri3_surf->drawable,
+                          msc_serial,
+                          target_msc,
+                          divisor,
+                          remainder);
+
+   xcb_flush(dri2_dpy->conn);
+
+   /* Wait for the event */
+   if (dri3_surf->special_event) {
+      while ((int32_t) (msc_serial - dri3_surf->recv_msc_serial) > 0) {
+         if (!dri3_wait_for_event(dri2_dpy, dri3_surf))
+            return EGL_FALSE;
+      }
+   }
+
+   *ust = dri3_surf->notify_ust;
+   *msc = dri3_surf->notify_msc;
+   *sbc = dri3_surf->recv_sbc;
+
+   return EGL_TRUE;
+}
+
+static EGLBoolean
+dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
+                     EGLuint64KHR *ust, EGLuint64KHR *msc,
+                     EGLuint64KHR *sbc)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(display);
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
+
+   return dri3_wait_for_msc(dri2_dpy, dri3_surf, 0, 0, 0, ust, msc, sbc);
+}
+
+/**
+ * Asks the driver to flush any queued work necessary for serializing with the
+ * X command stream, and optionally the slightly more strict requirement of
+ * glFlush() equivalence (which would require flushing even if nothing had
+ * been drawn to a window system framebuffer, for example).
+ */
+static void
+dri3_flush(struct dri2_egl_display *dri2_dpy,
+           struct dri3_egl_surface *dri3_surf,
+           unsigned flags,
+           enum __DRI2throttleReason throttle_reason)
+{
+   _EGLContext *ctx = _eglGetCurrentContext();
+   struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+
+   if (dri2_ctx)
+      (*dri2_dpy->flush->flush_with_flags)(dri2_ctx->dri_context,
+                                           dri3_surf->dri_drawable,
+                                           flags, throttle_reason);
+}
+
+static xcb_gcontext_t
+dri3_drawable_gc(struct dri2_egl_display *dri2_dpy,
+                 struct dri3_egl_surface *dri3_surf)
+{
+   if (!dri3_surf->gc) {
+      uint32_t v = 0;
+      xcb_create_gc(dri2_dpy->conn,
+                    (dri3_surf->gc = xcb_generate_id(dri2_dpy->conn)),
+                    dri3_surf->drawable,
+                    XCB_GC_GRAPHICS_EXPOSURES,
+                    &v);
+   }
+   return dri3_surf->gc;
+}
+
+
+static struct dri3_buffer *
+dri3_back_buffer(struct dri3_egl_surface *dri3_surf)
+{
+   return dri3_surf->buffers[DRI3_BACK_ID(dri3_surf->cur_back)];
+}
+
+static struct dri3_buffer *
+dri3_fake_front_buffer(struct dri3_egl_surface *dri3_surf)
+{
+   return dri3_surf->buffers[DRI3_FRONT_ID];
+}
+
+static void
+dri3_copy_area (xcb_connection_t *c  /**< */,
+                xcb_drawable_t    src_drawable  /**< */,
+                xcb_drawable_t    dst_drawable  /**< */,
+                xcb_gcontext_t    gc  /**< */,
+                int16_t           src_x  /**< */,
+                int16_t           src_y  /**< */,
+                int16_t           dst_x  /**< */,
+                int16_t           dst_y  /**< */,
+                uint16_t          width  /**< */,
+                uint16_t          height  /**< */)
+{
+   xcb_void_cookie_t cookie;
+
+   cookie = xcb_copy_area_checked(c,
+                                  src_drawable,
+                                  dst_drawable,
+                                  gc,
+                                  src_x,
+                                  src_y,
+                                  dst_x,
+                                  dst_y,
+                                  width,
+                                  height);
+   xcb_discard_reply(c, cookie.sequence);
+}
+
+/**
+ * Called by the driver when it needs to update the real front buffer with the
+ * contents of its fake front buffer.
+ */
+static void
+dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
+{
+#if 0
+   struct glx_context *gc;
+   struct dri3_drawable *pdraw = loaderPrivate;
+   struct dri3_screen *psc;
+
+   if (!pdraw)
+      return;
+
+   if (!pdraw->base.psc)
+      return;
+
+   psc = (struct dri3_screen *) pdraw->base.psc;
+
+   (void) __glXInitialize(psc->base.dpy);
+
+   gc = __glXGetCurrentContext();
+
+   dri3_flush(psc, pdraw, __DRI2_FLUSH_DRAWABLE, __DRI2_THROTTLE_FLUSHFRONT);
+
+   dri3_wait_gl(gc);
+#endif
+   /* FIXME */
+   (void) driDrawable;
+   (void) loaderPrivate;
+}
+
+static uint32_t
+dri3_cpp_for_format(uint32_t format) {
+   switch (format) {
+   case  __DRI_IMAGE_FORMAT_R8:
+      return 1;
+   case  __DRI_IMAGE_FORMAT_RGB565:
+   case  __DRI_IMAGE_FORMAT_GR88:
+      return 2;
+   case  __DRI_IMAGE_FORMAT_XRGB8888:
+   case  __DRI_IMAGE_FORMAT_ARGB8888:
+   case  __DRI_IMAGE_FORMAT_ABGR8888:
+   case  __DRI_IMAGE_FORMAT_XBGR8888:
+   case  __DRI_IMAGE_FORMAT_XRGB2101010:
+   case  __DRI_IMAGE_FORMAT_ARGB2101010:
+   case  __DRI_IMAGE_FORMAT_SARGB8:
+      return 4;
+   case  __DRI_IMAGE_FORMAT_NONE:
+   default:
+      return 0;
+   }
+}
+
+/** dri3_alloc_render_buffer
+ *
+ * Use the driver createImage function to construct a __DRIimage, then
+ * get a file descriptor for that and create an X pixmap from that
+ *
+ * Allocate an xshmfence for synchronization
+ */
+static struct dri3_buffer *
+dri3_alloc_render_buffer(struct dri2_egl_display *dri2_dpy, xcb_drawable_t draw,
+                         unsigned int format, int width, int height, int depth)
+{
+   struct dri3_buffer *buffer;
+   __DRIimage *pixmap_buffer;
+   xcb_pixmap_t pixmap;
+   xcb_sync_fence_t sync_fence;
+   struct xshmfence *shm_fence;
+   int buffer_fd, fence_fd;
+   int stride;
+
+   /* Create an xshmfence object and
+    * prepare to send that to the X server
+    */
+
+   fence_fd = xshmfence_alloc_shm();
+   if (fence_fd < 0) {
+      _eglLog(_EGL_WARNING, "DRI3 Fence object allocation failure %s\n", strerror(errno));
+      return NULL;
+   }
+   shm_fence = xshmfence_map_shm(fence_fd);
+   if (shm_fence == NULL) {
+      _eglLog(_EGL_WARNING, "DRI3 Fence object map failure %s\n", strerror(errno));
+      goto no_shm_fence;
+   }
+
+   /* Allocate the image from the driver
+    */
+   buffer = calloc(1, sizeof (struct dri3_buffer));
+   if (!buffer)
+      goto no_buffer;
+
+   buffer->cpp = dri3_cpp_for_format(format);
+   if (!buffer->cpp) {
+      _eglLog(_EGL_WARNING, "DRI3 buffer format %d invalid\n", format);
+      goto no_image;
+   }
+
+   if (!dri2_dpy->is_different_gpu) {
+      buffer->image = (*dri2_dpy->image->createImage) (dri2_dpy->dri_screen,
+                                                       width, height,
+                                                       format,
+                                                       __DRI_IMAGE_USE_SHARE |
+                                                       __DRI_IMAGE_USE_SCANOUT,
+                                                       buffer);
+      pixmap_buffer = buffer->image;
+
+      if (!buffer->image) {
+         _eglLog(_EGL_WARNING, "DRI3 gpu image creation failure\n");
+         goto no_image;
+      }
+   } else {
+      buffer->image = (*dri2_dpy->image->createImage) (dri2_dpy->dri_screen,
+                                                       width, height,
+                                                       format,
+                                                       0,
+                                                       buffer);
+
+      if (!buffer->image) {
+         _eglLog(_EGL_WARNING, "DRI3 other gpu image creation failure\n");
+         goto no_image;
+      }
+
+      buffer->linear_buffer = (*dri2_dpy->image->createImage) (dri2_dpy->dri_screen,
+                                                               width, height,
+                                                               format,
+                                                               __DRI_IMAGE_USE_SHARE |
+                                                               __DRI_IMAGE_USE_LINEAR,
+                                                               buffer);
+      pixmap_buffer = buffer->linear_buffer;
+
+      if (!buffer->linear_buffer) {
+         _eglLog(_EGL_WARNING, "DRI3 gpu linear image creation failure\n");
+         goto no_linear_buffer;
+      }
+   }
+
+   /* X wants the stride, so ask the image for it
+    */
+   if (!(*dri2_dpy->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, &stride)) {
+      _eglLog(_EGL_WARNING, "DRI3 get image stride failed\n");
+      goto no_buffer_attrib;
+   }
+
+   buffer->pitch = stride;
+
+   if (!(*dri2_dpy->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, &buffer_fd)) {
+      _eglLog(_EGL_WARNING, "DRI3 get image FD failed\n");
+      goto no_buffer_attrib;
+   }
+
+   xcb_dri3_pixmap_from_buffer(dri2_dpy->conn,
+                               (pixmap = xcb_generate_id(dri2_dpy->conn)),
+                               draw,
+                               buffer->size,
+                               width, height, buffer->pitch,
+                               depth, buffer->cpp * 8,
+                               buffer_fd);
+
+   xcb_dri3_fence_from_fd(dri2_dpy->conn,
+                          pixmap,
+                          (sync_fence = xcb_generate_id(dri2_dpy->conn)),
+                          false,
+                          fence_fd);
+
+   buffer->pixmap = pixmap;
+   buffer->own_pixmap = true;
+   buffer->sync_fence = sync_fence;
+   buffer->shm_fence = shm_fence;
+   buffer->width = width;
+   buffer->height = height;
+
+   /* Mark the buffer as idle
+    */
+   dri3_fence_set(buffer);
+
+   return buffer;
+
+no_buffer_attrib:
+   (*dri2_dpy->image->destroyImage)(pixmap_buffer);
+no_linear_buffer:
+   if (dri2_dpy->is_different_gpu)
+      (*dri2_dpy->image->destroyImage)(buffer->image);
+no_image:
+   free(buffer);
+no_buffer:
+   xshmfence_unmap_shm(shm_fence);
+no_shm_fence:
+   close(fence_fd);
+   _eglLog(_EGL_WARNING, "DRI3 alloc_render_buffer failed\n");
+   return NULL;
+}
+
+/** dri3_flush_present_events
+ *
+ * Process any present events that have been received from the X server
+ */
+static void
+dri3_flush_present_events(struct dri2_egl_display *dri2_dpy,
+                          struct dri3_egl_surface *dri3_surf)
+{
+   /* Check to see if any configuration changes have occurred
+    * since we were last invoked
+    */
+   if (dri3_surf->special_event) {
+      xcb_generic_event_t    *ev;
+
+      while ((ev = xcb_poll_for_special_event(dri2_dpy->conn,
+                                              dri3_surf->special_event)) != NULL) {
+         xcb_present_generic_event_t *ge = (void *) ev;
+         dri3_handle_present_event(dri2_dpy, dri3_surf, ge);
+      }
+   }
+}
+
+/** dri3_update_drawable
+ *
+ * Called the first time we use the drawable and then
+ * after we receive present configure notify events to
+ * track the geometry of the drawable
+ */
+static int
+dri3_update_drawable(__DRIdrawable *driDrawable,
+                     struct dri3_egl_surface *dri3_surf,
+                     struct dri2_egl_display *dri2_dpy)
+{
+   /* First time through, go get the current drawable geometry
+    */
+   if (dri3_surf->first_init) {
+      xcb_void_cookie_t                         cookie;
+      xcb_generic_error_t                       *error;
+      xcb_present_query_capabilities_cookie_t   present_capabilities_cookie;
+      xcb_present_query_capabilities_reply_t    *present_capabilities_reply;
+
+      dri3_surf->first_init = false;
+
+      /* Try to select for input on the window.
+       *
+       * If the drawable is a window, this will get our events
+       * delivered.
+       *
+       * Otherwise, we'll get a BadWindow error back from this request which
+       * will let us know that the drawable is a pixmap instead.
+       */
+
+
+      cookie = xcb_present_select_input_checked(dri2_dpy->conn,
+                                                (dri3_surf->eid = xcb_generate_id(dri2_dpy->conn)),
+                                                dri3_surf->drawable,
+                                                XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
+                                                XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
+                                                XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
+
+      present_capabilities_cookie = xcb_present_query_capabilities(dri2_dpy->conn,
+                                                                   dri3_surf->drawable);
+
+      /* Create an XCB event queue to hold present events outside of the usual
+       * application event queue
+       */
+      dri3_surf->special_event = xcb_register_for_special_xge(dri2_dpy->conn,
+                                                              &xcb_present_id,
+                                                              dri3_surf->eid,
+                                                              dri3_surf->stamp);
+
+      dri3_surf->is_pixmap = false;
+
+      /* Check to see if our select input call failed. If it failed with a
+       * BadWindow error, then assume the drawable is a pixmap. Destroy the
+       * special event queue created above and mark the drawable as a pixmap
+       */
+
+      error = xcb_request_check(dri2_dpy->conn, cookie);
+
+      present_capabilities_reply = xcb_present_query_capabilities_reply(dri2_dpy->conn,
+                                                                        present_capabilities_cookie,
+                                                                        NULL);
+
+      if (present_capabilities_reply) {
+         dri3_surf->present_capabilities = present_capabilities_reply->capabilities;
+         free(present_capabilities_reply);
+      } else
+         dri3_surf->present_capabilities = 0;
+
+      if (error) {
+         if (error->error_code != BadWindow) {
+            free(error);
+            return false;
+         }
+         dri3_surf->is_pixmap = true;
+         xcb_unregister_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+         dri3_surf->special_event = NULL;
+      }
+   }
+   dri3_flush_present_events(dri2_dpy, dri3_surf);
+   return true;
+}
+
+/* the DRIimage createImage function takes __DRI_IMAGE_FORMAT codes, while
+ * the createImageFromFds call takes __DRI_IMAGE_FOURCC codes. To avoid
+ * complete confusion, just deal in __DRI_IMAGE_FORMAT codes for now and
+ * translate to __DRI_IMAGE_FOURCC codes in the call to createImageFromFds
+ */
+static int
+image_format_to_fourcc(int format)
+{
+
+   /* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */
+   switch (format) {
+   case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
+   case __DRI_IMAGE_FORMAT_RGB565: return __DRI_IMAGE_FOURCC_RGB565;
+   case __DRI_IMAGE_FORMAT_XRGB8888: return __DRI_IMAGE_FOURCC_XRGB8888;
+   case __DRI_IMAGE_FORMAT_ARGB8888: return __DRI_IMAGE_FOURCC_ARGB8888;
+   case __DRI_IMAGE_FORMAT_ABGR8888: return __DRI_IMAGE_FOURCC_ABGR8888;
+   case __DRI_IMAGE_FORMAT_XBGR8888: return __DRI_IMAGE_FOURCC_XBGR8888;
+   }
+   return 0;
+}
+
+/** dri3_get_pixmap_buffer
+ *
+ * Get the DRM object for a pixmap from the X server and
+ * wrap that with a __DRIimage structure using createImageFromFds
+ */
+static struct dri3_buffer *
+dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
+                       unsigned int format,
+                       enum dri3_buffer_type buffer_type,
+                       struct dri3_egl_surface *dri3_surf,
+                       struct dri2_egl_display *dri2_dpy)
+{
+   int                                  buf_id = dri3_pixmap_buf_id(buffer_type);
+   struct dri3_buffer                   *buffer = dri3_surf->buffers[buf_id];
+   xcb_drawable_t                       pixmap;
+   xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
+   xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
+   int                                  *fds;
+   xcb_sync_fence_t                     sync_fence;
+   struct xshmfence                     *shm_fence;
+   int                                  fence_fd;
+   __DRIimage                           *image_planar;
+   int                                  stride, offset;
+
+   if (buffer)
+      return buffer;
+
+   pixmap = dri3_surf->drawable;
+
+   buffer = calloc(1, sizeof (struct dri3_buffer));
+   if (!buffer)
+      goto no_buffer;
+
+   fence_fd = xshmfence_alloc_shm();
+   if (fence_fd < 0)
+      goto no_fence;
+   shm_fence = xshmfence_map_shm(fence_fd);
+   if (shm_fence == NULL) {
+      close (fence_fd);
+      goto no_fence;
+   }
+
+   xcb_dri3_fence_from_fd(dri2_dpy->conn,
+                          pixmap,
+                          (sync_fence = xcb_generate_id(dri2_dpy->conn)),
+                          false,
+                          fence_fd);
+
+   /* Get an FD for the pixmap object
+    */
+   bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, pixmap);
+   bp_reply = xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn,
+                                                bp_cookie, NULL);
+   if (!bp_reply)
+      goto no_image;
+   fds = xcb_dri3_buffer_from_pixmap_reply_fds(dri2_dpy->conn, bp_reply);
+
+   stride = bp_reply->stride;
+   offset = 0;
+
+   /* createImageFromFds creates a wrapper __DRIimage structure which
+    * can deal with multiple planes for things like Yuv images. So, once
+    * we've gotten the planar wrapper, pull the single plane out of it and
+    * discard the wrapper.
+    */
+   image_planar = (*dri2_dpy->image->createImageFromFds) (dri2_dpy->dri_screen,
+                                                          bp_reply->width,
+                                                          bp_reply->height,
+                                                          image_format_to_fourcc(format),
+                                                          fds, 1,
+                                                          &stride, &offset, buffer);
+   close(fds[0]);
+   if (!image_planar)
+      goto no_image;
+
+   buffer->image = (*dri2_dpy->image->fromPlanar)(image_planar, 0, buffer);
+
+   (*dri2_dpy->image->destroyImage)(image_planar);
+
+   if (!buffer->image)
+      goto no_image;
+
+   buffer->pixmap = pixmap;
+   buffer->own_pixmap = false;
+   buffer->width = bp_reply->width;
+   buffer->height = bp_reply->height;
+   buffer->buffer_type = buffer_type;
+   buffer->shm_fence = shm_fence;
+   buffer->sync_fence = sync_fence;
+
+   dri3_surf->buffers[buf_id] = buffer;
+   return buffer;
+
+no_image:
+   xcb_sync_destroy_fence(dri2_dpy->conn, sync_fence);
+   xshmfence_unmap_shm(shm_fence);
+no_fence:
+   free(buffer);
+no_buffer:
+   return NULL;
+}
+
+/** dri3_find_back
+ *
+ * Find an idle back buffer. If there isn't one, then
+ * wait for a present idle notify event from the X server
+ */
+static int
+dri3_find_back(struct dri2_egl_display *dri2_dpy,
+               struct dri3_egl_surface *dri3_surf)
+{
+   int  b;
+   xcb_generic_event_t *ev;
+   xcb_present_generic_event_t *ge;
+
+   for (;;) {
+      for (b = 0; b < dri3_surf->num_back; b++) {
+         int id = DRI3_BACK_ID((b + dri3_surf->cur_back) % dri3_surf->num_back);
+         struct dri3_buffer *buffer = dri3_surf->buffers[id];
+
+         if (!buffer || !buffer->busy) {
+            dri3_surf->cur_back = id;
+            return id;
+         }
+      }
+      xcb_flush(dri2_dpy->conn);
+      ev = xcb_wait_for_special_event(dri2_dpy->conn, dri3_surf->special_event);
+      if (!ev)
+         return -1;
+      ge = (void *) ev;
+      dri3_handle_present_event(dri2_dpy, dri3_surf, ge);
+   }
+}
+
+/** dri3_get_buffer
+ *
+ * Find a front or back buffer, allocating new ones as necessary
+ */
+static struct dri3_buffer *
+dri3_get_buffer(__DRIdrawable *driDrawable,
+                unsigned int format,
+                enum dri3_buffer_type buffer_type,
+                struct dri3_egl_surface *dri3_surf,
+                struct dri2_egl_display *dri2_dpy)
+{
+   struct dri3_buffer      *buffer;
+   _EGLContext *ctx = _eglGetCurrentContext();
+   struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+   int                  buf_id;
+
+   if (buffer_type == dri3_buffer_back) {
+      buf_id = dri3_find_back(dri2_dpy, dri3_surf);
+
+      if (buf_id < 0)
+         return NULL;
+   } else {
+      buf_id = DRI3_FRONT_ID;
+   }
+
+   buffer = dri3_surf->buffers[buf_id];
+
+   /* Allocate a new buffer if there isn't an old one, or if that
+    * old one is the wrong size
+    */
+   if (!buffer || buffer->width != dri3_surf->base.Width ||
+       buffer->height != dri3_surf->base.Height) {
+      struct dri3_buffer   *new_buffer;
+
+      /* Allocate the new buffers
+       */
+      new_buffer = dri3_alloc_render_buffer(dri2_dpy,
+                                            dri3_surf->drawable,
+                                            format,
+                                            dri3_surf->base.Width,
+                                            dri3_surf->base.Height,
+                                            dri3_surf->depth);
+      if (!new_buffer)
+         return NULL;
+
+      /* When resizing, copy the contents of the old buffer, waiting for that
+       * copy to complete using our fences before proceeding
+       */
+      switch (buffer_type) {
+      case dri3_buffer_back:
+         if (buffer) {
+            if (!buffer->linear_buffer) {
+               dri3_fence_reset(dri2_dpy->conn, new_buffer);
+               dri3_fence_await(dri2_dpy->conn, buffer);
+               dri3_copy_area(dri2_dpy->conn,
+                              buffer->pixmap,
+                              new_buffer->pixmap,
+                              dri3_drawable_gc(dri2_dpy, dri3_surf),
+                              0, 0, 0, 0,
+                              dri3_surf->base.Width,
+                              dri3_surf->base.Height);
+            dri3_fence_trigger(dri2_dpy->conn, new_buffer);
+            } else if (dri2_ctx->base.Resource.Display == dri3_surf->base.Resource.Display) {
+               dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+                                         new_buffer->image,
+                                         buffer->image,
+                                         0, 0, dri3_surf->base.Width,
+                                         dri3_surf->base.Height,
+                                         0, 0, dri3_surf->base.Width,
+                                         dri3_surf->base.Height, 0);
+            }
+            dri3_free_render_buffer(dri2_dpy, dri3_surf, buffer);
+         }
+         break;
+      case dri3_buffer_front:
+         dri3_fence_reset(dri2_dpy->conn, new_buffer);
+         dri3_copy_area(dri2_dpy->conn,
+                        dri3_surf->drawable,
+                        new_buffer->pixmap,
+                        dri3_drawable_gc(dri2_dpy, dri3_surf),
+                        0, 0, 0, 0,
+                        dri3_surf->base.Width,
+                        dri3_surf->base.Height);
+         dri3_fence_trigger(dri2_dpy->conn, new_buffer);
+
+         if (new_buffer->linear_buffer &&
+             dri2_ctx->base.Resource.Display == dri3_surf->base.Resource.Display) {
+            dri3_fence_await(dri2_dpy->conn, new_buffer);
+            dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+                                       new_buffer->image,
+                                       new_buffer->linear_buffer,
+                                       0, 0, dri3_surf->base.Width,
+                                       dri3_surf->base.Height,
+                                       0, 0, dri3_surf->base.Width,
+                                       dri3_surf->base.Height, 0);
+         }
+         break;
+      }
+      buffer = new_buffer;
+      buffer->buffer_type = buffer_type;
+      dri3_surf->buffers[buf_id] = buffer;
+   }
+   dri3_fence_await(dri2_dpy->conn, buffer);
+
+   /* Return the requested buffer */
+   return buffer;
+}
+
+/** dri3_free_buffers
+ *
+ * Free the front bufffer or all of the back buffers. Used
+ * when the application changes which buffers it needs
+ */
+static void
+dri3_free_buffers(__DRIdrawable *driDrawable,
+                  enum dri3_buffer_type buffer_type,
+                  struct dri3_egl_surface *dri3_surf,
+                  struct dri2_egl_display *dri2_dpy)
+{
+   struct dri3_buffer      *buffer;
+   int                  first_id;
+   int                  n_id;
+   int                  buf_id;
+
+   switch (buffer_type) {
+   case dri3_buffer_back:
+      first_id = DRI3_BACK_ID(0);
+      n_id = DRI3_MAX_BACK;
+      break;
+   case dri3_buffer_front:
+      first_id = DRI3_FRONT_ID;
+      n_id = 1;
+   }
+
+   for (buf_id = first_id; buf_id < first_id + n_id; buf_id++) {
+      buffer = dri3_surf->buffers[buf_id];
+      if (buffer) {
+         dri3_free_render_buffer(dri2_dpy, dri3_surf, buffer);
+         dri3_surf->buffers[buf_id] = NULL;
+      }
+   }
+}
+
+/** dri3_get_buffers
+ *
+ * The published buffer allocation API.
+ * Returns all of the necessary buffers, allocating
+ * as needed.
+ */
+static int
+dri3_get_buffers(__DRIdrawable *driDrawable,
+                 unsigned int format,
+                 uint32_t *stamp,
+                 void *loaderPrivate,
+                 uint32_t buffer_mask,
+                 struct __DRIimageList *buffers)
+{
+   struct dri3_egl_surface *dri3_surf = loaderPrivate;
+   struct dri2_egl_display *dri2_dpy =
+      dri2_egl_display(dri3_surf->base.Resource.Display);
+   struct dri3_buffer   *front, *back;
+
+   buffers->image_mask = 0;
+   buffers->front = NULL;
+   buffers->back = NULL;
+
+   front = NULL;
+   back = NULL;
+
+   if (!dri3_update_drawable(driDrawable, dri3_surf, dri2_dpy))
+      return false;
+
+   /* pixmaps always have front buffers */
+   if (dri3_surf->is_pixmap)
+      buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
+
+   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
+      /* All pixmaps are owned by the server gpu.
+       * When we use a different gpu, we can't use the pixmap
+       * as buffer since it is potentially tiled a way
+       * our device can't understand. In this case, use
+       * a fake front buffer. Hopefully the pixmap
+       * content will get synced with the fake front
+       * buffer.
+       */
+      if (dri3_surf->is_pixmap && !dri2_dpy->is_different_gpu)
+         front = dri3_get_pixmap_buffer(driDrawable,
+                                        format,
+                                        dri3_buffer_front,
+                                        dri3_surf,
+                                        dri2_dpy);
+      else
+         front = dri3_get_buffer(driDrawable,
+                                 format,
+                                 dri3_buffer_front,
+                                 dri3_surf,
+                                 dri2_dpy);
+
+      if (!front)
+         return false;
+   } else {
+      dri3_free_buffers(driDrawable, dri3_buffer_front, dri3_surf, dri2_dpy);
+      dri3_surf->have_fake_front = 0;
+   }
+
+   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
+      back = dri3_get_buffer(driDrawable,
+                             format,
+                             dri3_buffer_back,
+                             dri3_surf,
+                             dri2_dpy);
+      if (!back)
+         return false;
+      dri3_surf->have_back = 1;
+   } else {
+      dri3_free_buffers(driDrawable, dri3_buffer_back, dri3_surf, dri2_dpy);
+      dri3_surf->have_back = 0;
+   }
+
+   if (front) {
+      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
+      buffers->front = front->image;
+      dri3_surf->have_fake_front = dri2_dpy->is_different_gpu || !dri3_surf->is_pixmap;
+   }
+
+   if (back) {
+      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
+      buffers->back = back->image;
+   }
+
+   dri3_surf->stamp = stamp;
+
+   return true;
+}
+
+const __DRIimageLoaderExtension dri3_image_loader_extension = {
+   .base = { __DRI_IMAGE_LOADER, 1 },
+
+   .getBuffers          = dri3_get_buffers,
+   .flushFrontBuffer    = dri3_flush_front_buffer,
+};
+
+static int
+dri3_get_ust(int64_t *ust)
+{
+   struct timeval tv;
+
+   if (ust == NULL)
+      return -EFAULT;
+
+   if (gettimeofday(&tv, NULL) == 0) {
+      ust[0] = (tv.tv_sec * 1000000) + tv.tv_usec;
+      return 0;
+   } else
+      return -errno;
+}
+
+static GLboolean
+dri3_get_msc_rate(__DRIdrawable *draw, int32_t *numerator,
+                  int32_t *denominator, void *loaderPrivate)
+{
+#ifdef XF86VIDMODE
+   dri3_egl_surface *dri3_surf = loaderPrivate;
+   struct dri2_egl_display *dri2_dpy =
+      dri2_egl_display(dri3_surf->base.Resource.Display);
+   XF86VidModeModeLine mode_line;
+   int dot_clock;
+   int i;
+
+   if (XF86VidModeQueryVersion(dri2_dpy->dpy, &i, &i) &&
+       XF86VidModeGetModeLine(dri2_dpy->dpy, dri2_dpy->screen,
+                              &dot_clock, &mode_line)) {
+      unsigned n = dot_clock * 1000;
+      unsigned d = mode_line.vtotal * mode_line.htotal;
+
+# define V_INTERLACE 0x010
+# define V_DBLSCAN   0x020
+
+      if (mode_line.flags & V_INTERLACE)
+         n *= 2;
+      else if (mode_line.flags & V_DBLSCAN)
+         d *= 2;
+
+      /* The OML_sync_control spec requires that if the refresh rate is a
+       * whole number, that the returned numerator be equal to the refresh
+       * rate and the denominator be 1.
+       */
+
+      if (n % d == 0) {
+         n /= d;
+         d = 1;
+      }
+      else {
+         static const unsigned f[] = { 13, 11, 7, 5, 3, 2, 0 };
+
+         /* This is a poor man's way to reduce a fraction.  It's far from
+          * perfect, but it will work well enough for this situation.
+          */
+
+         for (i = 0; f[i] != 0; i++) {
+            while (n % f[i] == 0 && d % f[i] == 0) {
+               d /= f[i];
+               n /= f[i];
+            }
+         }
+      }
+
+      *numerator = n;
+      *denominator = d;
+
+      return True;
+
+#endif
+   return False;
+}
+
+const __DRIsystemTimeExtension dri3_system_time_extension = {
+   .base = { __DRI_SYSTEM_TIME, 1 },
+
+   .getUST              = dri3_get_ust,
+   .getMSCRate          = dri3_get_msc_rate,
+};
+
+/** dri3_swap_buffers_msc
+ *
+ * Make the current back buffer visible using the present extension
+ */
+static int64_t
+dri3_swap_buffers_msc(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw,
+                      int64_t target_msc, int64_t divisor,
+                      int64_t remainder)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   struct dri3_buffer *back;
+   _EGLContext *ctx = _eglGetCurrentContext();
+   struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+   int64_t ret = 0;
+   uint32_t options = XCB_PRESENT_OPTION_NONE;
+
+   dri2_flush_drawable_for_swapbuffers(disp, draw);
+
+   back = dri3_surf->buffers[DRI3_BACK_ID(dri3_surf->cur_back)];
+   if (dri2_dpy->is_different_gpu && back) {
+      /* Update the linear buffer before presenting the pixmap */
+      dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+                                 back->linear_buffer,
+                                 back->image,
+                                 0, 0, back->width,
+                                 back->height,
+                                 0, 0, back->width,
+                                 back->height, __BLIT_FLAG_FLUSH);
+      /* Update the fake front */
+      if (dri3_surf->have_fake_front)
+         dri2_dpy->image->blitImage(dri2_ctx->dri_context,
+                                    dri3_surf->buffers[DRI3_FRONT_ID]->image,
+                                    back->image,
+                                    0, 0, dri3_surf->base.Width,
+                                    dri3_surf->base.Height,
+                                    0, 0, dri3_surf->base.Width,
+                                    dri3_surf->base.Height,
+                                    __BLIT_FLAG_FLUSH);
+   }
+
+   dri3_flush_present_events(dri2_dpy, dri3_surf);
+
+   if (back && !dri3_surf->is_pixmap) {
+      dri3_fence_reset(dri2_dpy->conn, back);
+
+      /* Compute when we want the frame shown by taking the last known successful
+       * MSC and adding in a swap interval for each outstanding swap request.
+       * target_msc=divisor=remainder=0 means "Use glXSwapBuffers() semantic"
+       */
+      ++dri3_surf->send_sbc;
+      if (target_msc == 0 && divisor == 0 && remainder == 0)
+         target_msc = dri3_surf->msc + dri3_surf->base.SwapInterval * (dri3_surf->send_sbc - dri3_surf->recv_sbc);
+      else if (divisor == 0 && remainder > 0) {
+         /*
+          *     "If <divisor> = 0, the swap will occur when MSC becomes
+          *      greater than or equal to <target_msc>."
+          *
+          * Note that there's no mention of the remainder.  The Present extension
+          * throws BadValue for remainder != 0 with divisor == 0, so just drop
+          * the passed in value.
+          */
+         remainder = 0;
+      }
+
+      /* From the EGL 1.4 spec (page 53):
+       *
+       *     "If <interval> is set to a value of 0, buffer swaps are not
+       *      synchronized to a video frame."
+       *
+       * Implementation note: It is possible to enable triple buffering behaviour
+       * by not using XCB_PRESENT_OPTION_ASYNC, but this should not be the default.
+       */
+      if (dri3_surf->base.SwapInterval == 0)
+          options |= XCB_PRESENT_OPTION_ASYNC;
+
+      back->busy = 1;
+      back->last_swap = dri3_surf->send_sbc;
+      xcb_present_pixmap(dri2_dpy->conn,
+                         dri3_surf->drawable,
+                         back->pixmap,
+                         (uint32_t) dri3_surf->send_sbc,
+                         0,                                    /* valid */
+                         0,                                    /* update */
+                         0,                                    /* x_off */
+                         0,                                    /* y_off */
+                         None,                                 /* target_crtc */
+                         None,
+                         back->sync_fence,
+                         options,
+                         target_msc,
+                         divisor,
+                         remainder, 0, NULL);
+      ret = (int64_t) dri3_surf->send_sbc;
+
+      /* If there's a fake front, then copy the source back buffer
+       * to the fake front to keep it up to date. This needs
+       * to reset the fence and make future users block until
+       * the X server is done copying the bits
+       */
+      if (dri3_surf->have_fake_front && !dri2_dpy->is_different_gpu) {
+         dri3_fence_reset(dri2_dpy->conn, dri3_surf->buffers[DRI3_FRONT_ID]);
+         dri3_copy_area(dri2_dpy->conn,
+                        back->pixmap,
+                        dri3_surf->buffers[DRI3_FRONT_ID]->pixmap,
+                        dri3_drawable_gc(dri2_dpy, dri3_surf),
+                        0, 0, 0, 0,
+                        dri3_surf->base.Width,
+                        dri3_surf->base.Height);
+         dri3_fence_trigger(dri2_dpy->conn, dri3_surf->buffers[DRI3_FRONT_ID]);
+      }
+      xcb_flush(dri2_dpy->conn);
+      if (dri3_surf->stamp)
+         ++(*dri3_surf->stamp);
+   }
+
+   (*dri2_dpy->flush->invalidate)(dri3_surf->dri_drawable);
+
+   return ret;
+}
+
+static EGLBoolean
+dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
+{
+   return dri3_swap_buffers_msc(drv, disp, draw, 0, 0, 0) != -1;
+}
+
+static int
+dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+   int back_id = DRI3_BACK_ID(dri3_find_back(dri2_dpy, dri3_surf));
+
+   if (back_id < 0 || !dri3_surf->buffers[back_id])
+      return 0;
+
+   if (dri3_surf->buffers[back_id]->last_swap != 0)
+      return dri3_surf->send_sbc - dri3_surf->buffers[back_id]->last_swap + 1;
+   else
+      return 0;
+}
+
+/* FIXME: Is this right? Seems problematic for WL_bind_wayland_display */
+static int
+dri3_authenticate(_EGLDisplay *disp, uint32_t id)
+{
+   return 0;
+}
+
+struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
+   .authenticate = dri3_authenticate,
+   .create_window_surface = dri3_create_window_surface,
+   .create_pixmap_surface = dri3_create_pixmap_surface,
+   .create_pbuffer_surface = dri3_create_pbuffer_surface,
+   .destroy_surface = dri3_destroy_surface,
+   .create_image = dri2_create_image_khr,
+   .swap_interval = dri3_set_swap_interval,
+   .swap_buffers = dri3_swap_buffers,
+   .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
+   .swap_buffers_region = dri2_fallback_swap_buffers_region,
+   .post_sub_buffer = dri2_fallback_post_sub_buffer,
+   .copy_buffers = dri2_fallback_copy_buffers,
+   .query_buffer_age = dri3_query_buffer_age,
+   .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,
+   .get_sync_values = dri3_get_sync_values,
+};
+
+/** dri3_open
+ *
+ * Wrapper around xcb_dri3_open
+ */
+static int
+dri3_open(xcb_connection_t *conn,
+          xcb_window_t root,
+          uint32_t provider)
+{
+   xcb_dri3_open_cookie_t       cookie;
+   xcb_dri3_open_reply_t        *reply;
+   int                          fd;
+
+   cookie = xcb_dri3_open(conn,
+                          root,
+                          provider);
+
+   reply = xcb_dri3_open_reply(conn, cookie, NULL);
+   if (!reply)
+      return -1;
+
+   if (reply->nfd != 1) {
+      free(reply);
+      return -1;
+   }
+
+   fd = xcb_dri3_open_reply_fds(conn, reply)[0];
+   fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+   return fd;
+}
+
+EGLBoolean
+dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
+{
+   xcb_dri3_query_version_reply_t *dri3_query;
+   xcb_dri3_query_version_cookie_t dri3_query_cookie;
+   xcb_present_query_version_reply_t *present_query;
+   xcb_present_query_version_cookie_t present_query_cookie;
+   xcb_generic_error_t *error;
+   xcb_screen_iterator_t s;
+   xcb_screen_t *screen;
+   const xcb_query_extension_reply_t *extension;
+
+   xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
+   xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
+
+   extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id);
+   if (!(extension && extension->present))
+      return EGL_FALSE;
+
+   extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id);
+   if (!(extension && extension->present))
+      return EGL_FALSE;
+
+   dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn,
+                                              XCB_DRI3_MAJOR_VERSION,
+                                              XCB_DRI3_MINOR_VERSION);
+
+   present_query_cookie = xcb_present_query_version(dri2_dpy->conn,
+                                                    XCB_PRESENT_MAJOR_VERSION,
+                                                    XCB_PRESENT_MINOR_VERSION);
+
+   /* FIXME: a little bit memory leak here */
+   dri3_query =
+      xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error);
+   if (dri3_query == NULL || error != NULL) {
+      _eglLog(_EGL_WARNING, "DRI2: failed to query dri3 version");
+      free(error);
+      return EGL_FALSE;
+   }
+   dri2_dpy->dri3_major = dri3_query->major_version;
+   dri2_dpy->dri3_minor = dri3_query->minor_version;
+   free(dri3_query);
+
+   present_query =
+      xcb_present_query_version_reply(dri2_dpy->conn,
+                                      present_query_cookie, &error);
+   if (present_query == NULL || error != NULL) {
+      _eglLog(_EGL_WARNING, "DRI2: failed to query Present version");
+      free(error);
+      return EGL_FALSE;
+   }
+   dri2_dpy->present_major = present_query->major_version;
+   dri2_dpy->present_minor = present_query->minor_version;
+   free(present_query);
+
+   s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn));
+   screen = get_xcb_screen(s, dri2_dpy->screen);
+   if (!screen) {
+      _eglError(EGL_BAD_NATIVE_WINDOW, "dri3_x11_connect");
+      return EGL_FALSE;
+   }
+
+   dri2_dpy->fd = dri3_open(dri2_dpy->conn, screen->root, 0);
+   if (dri2_dpy->fd < 0) {
+      int conn_error = xcb_connection_has_error(dri2_dpy->conn);
+      _eglLog(_EGL_WARNING, "DRI2: Screen seem not DRI3 capable");
+
+      if (conn_error)
+         _eglLog(_EGL_WARNING, "DRI2: Failed to initialize DRI3");
+
+      return EGL_FALSE;
+   }
+
+   dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
+
+   dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd, 0);
+   if (!dri2_dpy->driver_name) {
+      _eglLog(_EGL_WARNING, "DRI2: No driver found");
+      close(dri2_dpy->fd);
+      return EGL_FALSE;
+   }
+
+   dri2_dpy->device_name = loader_get_device_name_for_fd(dri2_dpy->fd);
+   if (!dri2_dpy->device_name) {
+      _eglLog(_EGL_WARNING, "DRI2: Cannot find device name");
+      close(dri2_dpy->fd);
+      return EGL_FALSE;
+   }
+
+   return EGL_TRUE;
+}
diff --git a/src/egl/drivers/dri2/platform_x11_dri3.h b/src/egl/drivers/dri2/platform_x11_dri3.h
new file mode 100644
index 0000000..efdfcdb
--- /dev/null
+++ b/src/egl/drivers/dri2/platform_x11_dri3.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef EGL_X11_DRI3_INCLUDED
+#define EGL_X11_DRI3_INCLUDED
+
+#include <stdbool.h>
+#include <xcb/xcb.h>
+#include <xcb/present.h>
+
+#include "egl_dri2.h"
+
+_EGL_DRIVER_TYPECAST(dri3_egl_surface, _EGLSurface, obj)
+enum dri3_buffer_type {
+   dri3_buffer_back = 0,
+   dri3_buffer_front = 1
+};
+
+struct dri3_buffer {
+   __DRIimage   *image;
+   __DRIimage   *linear_buffer;
+   uint32_t     pixmap;
+
+   /* Synchronization between the client and X server is done using an
+    * xshmfence that is mapped into an X server SyncFence. This lets the
+    * client check whether the X server is done using a buffer with a simple
+    * xshmfence call, rather than going to read X events from the wire.
+    *
+    * However, we can only wait for one xshmfence to be triggered at a time,
+    * so we need to know *which* buffer is going to be idle next. We do that
+    * by waiting for a PresentIdleNotify event. When that event arrives, the
+    * 'busy' flag gets cleared and the client knows that the fence has been
+    * triggered, and that the wait call will not block.
+    */
+
+   uint32_t     sync_fence;     /* XID of X SyncFence object */
+   struct xshmfence *shm_fence; /* pointer to xshmfence object */
+   bool         busy;           /* Set on swap, cleared on IdleNotify */
+   bool         own_pixmap;     /* We allocated the pixmap ID, free on destroy */
+
+   uint32_t     size;
+   uint32_t     pitch;
+   uint32_t     cpp;
+   uint32_t     flags;
+   uint32_t     width, height;
+   uint64_t     last_swap;
+
+   enum dri3_buffer_type        buffer_type;
+};
+
+
+#define DRI3_MAX_BACK   4
+#define DRI3_BACK_ID(i) (i)
+#define DRI3_FRONT_ID   (DRI3_MAX_BACK)
+
+static inline int
+dri3_pixmap_buf_id(enum dri3_buffer_type buffer_type)
+{
+   if (buffer_type == dri3_buffer_back)
+      return DRI3_BACK_ID(0);
+   else
+      return DRI3_FRONT_ID;
+}
+
+#define DRI3_NUM_BUFFERS        (1 + DRI3_MAX_BACK)
+
+struct dri3_egl_surface {
+   _EGLSurface base;
+   __DRIdrawable *dri_drawable;
+   xcb_drawable_t drawable;
+   int depth;
+   uint8_t have_back;
+   uint8_t have_fake_front;
+   uint8_t is_pixmap;
+   uint8_t flipping;
+
+   /* Present extension capabilities
+    */
+   uint32_t present_capabilities;
+
+   /* SBC numbers are tracked by using the serial numbers
+    * in the present request and complete events
+    */
+   uint64_t send_sbc;
+   uint64_t recv_sbc;
+
+   /* Last received UST/MSC values for pixmap present complete */
+   uint64_t ust, msc;
+
+   /* Last received UST/MSC values from present notify msc event */
+   uint64_t notify_ust, notify_msc;
+
+   /* Serial numbers for tracking wait_for_msc events */
+   uint32_t send_msc_serial;
+   uint32_t recv_msc_serial;
+
+   struct dri3_buffer *buffers[DRI3_NUM_BUFFERS];
+   int cur_back;
+   int num_back;
+
+   uint32_t *stamp;
+
+   xcb_present_event_t eid;
+   xcb_gcontext_t gc;
+   xcb_special_event_t *special_event;
+
+   /* LIBGL_SHOW_FPS support */
+   uint64_t previous_ust;
+   unsigned frames;
+
+   bool first_init;
+};
+
+extern const __DRIimageLoaderExtension dri3_image_loader_extension;
+extern const __DRIsystemTimeExtension dri3_system_time_extension;
+extern struct dri2_egl_display_vtbl dri3_x11_display_vtbl;
+
+EGLBoolean
+dri3_x11_connect(struct dri2_egl_display *dri2_dpy);
+
+#endif
-- 
2.4.4



More information about the mesa-dev mailing list