egl/x11: Implement dri3 support with loader's dri3 helper
authorBoyan Ding <boyan.j.ding@gmail.com>
Tue, 21 Jul 2015 15:44:00 +0000 (23:44 +0800)
committerMartin Peres <martin.peres@linux.intel.com>
Tue, 17 Nov 2015 15:26:20 +0000 (17:26 +0200)
v2: From Martin Peres
 - Tell we are compiling the dri3 backend in configure.ac
 - Update the Makefile.am
 - get rid of the LIBDRM_HAS_RENDERNODE_SUPPORT macro
 - fix some warnings related to EGLuint64KHR to int64_t conversions
 - use dri2_get_dri_config to get the __DRIconfig instead of open-coding it
 - replace the occasional tabs with spaces

v3: From Martin Peres
 - fix and indent problem (Matt Turner)
 - drop the authenticate function, use NULL in the vtable instead (Emil)
 - drop some useless includes (Emil Velikov)
 - mandate libdrm (Emil Velikov)
 - link to xcb-dri3 (Kristian Høgsberg)
 - convert to the new loader interface for drwable (Kristian)
 - remove some dead code after the dropping of some vfuncs (Kristian)
 - add a comment on the topic of rendering to the frontbuffer

v4: From Martin Peres
 - do not expose the preserved swap behavior (Acked by Eric Anholt)

Signed-off-by: Boyan Ding <boyan.j.ding@gmail.com>
Signed-off-by: Martin Peres <martin.peres@linux.intel.com>
Reviewed-by: Kristian Høgsberg <krh@bitplanet.net>
Reviewed-by: Emil Velikov <emil.velikov@collabora.co.uk>
configure.ac
src/egl/Makefile.am
src/egl/drivers/dri2/egl_dri2.c
src/egl/drivers/dri2/egl_dri2.h
src/egl/drivers/dri2/platform_x11.c
src/egl/drivers/dri2/platform_x11_dri3.c [new file with mode: 0644]
src/egl/drivers/dri2/platform_x11_dri3.h [new file with mode: 0644]

index 9ea9ab22346aa335810a404ba77ae22ad3f8baad..32fb989a898c000199aafdfbbb990d2c4a146b01 100644 (file)
@@ -1568,6 +1568,12 @@ 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
+                HAVE_EGL_DRIVER_DRI3=1
+                if test "x$enable_shared_glapi" = xno; then
+                    AC_MSG_ERROR([egl_dri3 requires --enable-shared-glapi])
+                fi
+            fi
         else
             # Avoid building an "empty" libEGL. Drop/update this
             # when other backends (haiku?) come along.
@@ -2520,6 +2526,9 @@ if test "$enable_egl" = yes; then
     if test "x$HAVE_EGL_DRIVER_DRI2" != "x"; then
         egl_drivers="$egl_drivers builtin:egl_dri2"
     fi
+    if test "x$HAVE_EGL_DRIVER_DRI3" != "x"; then
+        egl_drivers="$egl_drivers builtin:egl_dri3"
+    fi
 
     echo "        EGL drivers:    $egl_drivers"
 fi
index 5c2ba301ffb349f21dcd564e50a14aebe9b24ff5..88fe13acbd41b6d678099367c7d94c02d8ef4013 100644 (file)
@@ -47,12 +47,19 @@ libEGL_la_LDFLAGS = \
        $(LD_NO_UNDEFINED)
 
 dri2_backend_FILES =
+dri3_backend_FILES =
 
 if HAVE_EGL_PLATFORM_X11
 AM_CFLAGS += -DHAVE_X11_PLATFORM
 AM_CFLAGS += $(XCB_DRI2_CFLAGS)
 libEGL_la_LIBADD += $(XCB_DRI2_LIBS)
 dri2_backend_FILES += drivers/dri2/platform_x11.c
+
+if HAVE_DRI3
+dri3_backend_FILES += \
+       drivers/dri2/platform_x11_dri3.c
+libEGL_la_LIBADD += $(top_builddir)/src/loader/libloader_dri3_helper.la
+endif
 endif
 
 if HAVE_EGL_PLATFORM_WAYLAND
@@ -88,7 +95,8 @@ AM_CFLAGS += \
 
 libEGL_la_SOURCES += \
        $(dri2_backend_core_FILES) \
-       $(dri2_backend_FILES)
+       $(dri2_backend_FILES) \
+       $(dri3_backend_FILES)
 
 libEGL_la_LIBADD += $(top_builddir)/src/loader/libloader.la
 libEGL_la_LIBADD += $(DLOPEN_LIBS) $(LIBDRM_LIBS)
index ceff01ff523d10bac99e6de5bcbc36fc047e2f92..ba16b94e651e08f74d1e09b1923da9ea9772a43e 100644 (file)
@@ -352,6 +352,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) },
@@ -493,6 +499,25 @@ dri2_open_driver(_EGLDisplay *disp)
    return extensions;
 }
 
+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);
+      return EGL_FALSE;
+   }
+   dri2_dpy->driver_extensions = extensions;
+
+   return EGL_TRUE;
+}
+
 EGLBoolean
 dri2_load_driver(_EGLDisplay *disp)
 {
@@ -550,7 +575,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);
@@ -570,7 +597,7 @@ 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;
 
@@ -578,7 +605,8 @@ dri2_setup_screen(_EGLDisplay *disp)
                                    __DRI2_RENDERER_HAS_FRAMEBUFFER_SRGB))
       disp->Extensions.KHR_gl_colorspace = 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;
 
@@ -641,7 +669,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,
@@ -677,7 +712,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 {
@@ -1024,7 +1059,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;
index c3c9fc0166c621efadd05ca68a4ce3765b11eb1a..52ad92b182d300070bda41570fee97dd7bc9caee 100644 (file)
 #include <xcb/dri2.h>
 #include <xcb/xfixes.h>
 #include <X11/Xlib-xcb.h>
+
+#ifdef HAVE_DRI3
+#include "loader_dri3_helper.h"
+#endif
 #endif
 
 #ifdef HAVE_WAYLAND_PLATFORM
@@ -160,6 +164,7 @@ struct dri2_egl_display
    const __DRIconfig       **driver_configs;
    void                     *driver;
    const __DRIcoreExtension       *core;
+   const __DRIimageDriverExtension *image_driver;
    const __DRIdri2Extension       *dri2;
    const __DRIswrastExtension     *swrast;
    const __DRI2flushExtension     *flush;
@@ -192,6 +197,9 @@ struct dri2_egl_display
 #ifdef HAVE_X11_PLATFORM
    xcb_connection_t         *conn;
    int                      screen;
+#ifdef HAVE_DRI3
+   struct loader_dri3_extensions loader_dri3_ext;
+#endif
 #endif
 
 #ifdef HAVE_WAYLAND_PLATFORM
@@ -205,8 +213,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
@@ -326,6 +335,9 @@ dri2_setup_screen(_EGLDisplay *disp);
 EGLBoolean
 dri2_load_driver_swrast(_EGLDisplay *disp);
 
+EGLBoolean
+dri2_load_driver_dri3(_EGLDisplay *disp);
+
 EGLBoolean
 dri2_create_screen(_EGLDisplay *disp);
 
index e75dcb90699f245887c1cb446efc8bd4a1963dd3..d291b478a25d329454f99983240169225012e186 100644 (file)
 #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);
@@ -703,7 +707,7 @@ dri2_x11_local_authenticate(_EGLDisplay *disp)
 
 static EGLBoolean
 dri2_x11_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy,
-                                 _EGLDisplay *disp)
+                                 _EGLDisplay *disp, bool supports_preserved)
 {
    xcb_screen_iterator_t s;
    xcb_depth_iterator_t d;
@@ -724,8 +728,10 @@ dri2_x11_add_configs_for_visuals(struct dri2_egl_display *dri2_dpy,
    surface_type =
       EGL_WINDOW_BIT |
       EGL_PIXMAP_BIT |
-      EGL_PBUFFER_BIT |
-      EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
+      EGL_PBUFFER_BIT;
+
+   if (supports_preserved)
+      surface_type |= EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
 
    while (d.rem > 0) {
       EGLBoolean class_added[6] = { 0, };
@@ -1181,7 +1187,7 @@ dri2_initialize_x11_swrast(_EGLDriver *drv, _EGLDisplay *disp)
    if (!dri2_create_screen(disp))
       goto cleanup_driver;
 
-   if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp))
+   if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp, true))
       goto cleanup_configs;
 
    /* Fill vtbl last to prevent accidentally calling virtual function during
@@ -1252,6 +1258,96 @@ 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->conn = xcb_connect(0, &dri2_dpy->screen);
+      dri2_dpy->own_device = true;
+   } else {
+      Display *dpy = disp->PlatformDisplay;
+
+      dri2_dpy->conn = XGetXCBConnection(dpy);
+      dri2_dpy->screen = DefaultScreen(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] = &use_invalidate.base;
+   dri2_dpy->extensions[2] = &image_lookup_extension.base;
+   dri2_dpy->extensions[3] = 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, false))
+         goto cleanup_configs;
+   }
+
+   dri2_dpy->loader_dri3_ext.core = dri2_dpy->core;
+   dri2_dpy->loader_dri3_ext.image_driver = dri2_dpy->image_driver;
+   dri2_dpy->loader_dri3_ext.flush = dri2_dpy->flush;
+   dri2_dpy->loader_dri3_ext.tex_buffer = dri2_dpy->tex_buffer;
+   dri2_dpy->loader_dri3_ext.image = dri2_dpy->image;
+   dri2_dpy->loader_dri3_ext.config = dri2_dpy->config;
+
+   /* 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)
 {
@@ -1323,7 +1419,7 @@ dri2_initialize_x11_dri2(_EGLDriver *drv, _EGLDisplay *disp)
    disp->Extensions.WL_bind_wayland_display = EGL_TRUE;
 #endif
 
-   if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp))
+   if (!dri2_x11_add_configs_for_visuals(dri2_dpy, disp, true))
       goto cleanup_configs;
 
    /* Fill vtbl last to prevent accidentally calling virtual function during
@@ -1357,9 +1453,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_DISABLE") != 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 (file)
index 0000000..0b95e4d
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+ * Copyright © 2015 Boyan Ding
+ *
+ * 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 <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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"
+#include "loader_dri3_helper.h"
+
+static struct dri3_egl_surface *
+loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) {
+   size_t offset = offsetof(struct dri3_egl_surface, loader_drawable);
+   return (struct dri3_egl_surface *)(((void*) draw) - offset);
+}
+
+static int
+egl_dri3_get_swap_interval(struct loader_dri3_drawable *draw)
+{
+   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
+
+   return dri3_surf->base.SwapInterval;
+}
+
+static int
+egl_dri3_clamp_swap_interval(struct loader_dri3_drawable *draw, int interval)
+{
+   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
+
+   if (interval > dri3_surf->base.Config->MaxSwapInterval)
+      interval = dri3_surf->base.Config->MaxSwapInterval;
+   else if (interval < dri3_surf->base.Config->MinSwapInterval)
+      interval = dri3_surf->base.Config->MinSwapInterval;
+
+   return interval;
+}
+
+static void
+egl_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
+{
+   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
+
+   dri3_surf->base.SwapInterval = interval;
+}
+
+static void
+egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw,
+                           int width, int height)
+{
+   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
+
+   dri3_surf->base.Width = width;
+   dri3_surf->base.Height = height;
+}
+
+static bool
+egl_dri3_in_current_context(struct loader_dri3_drawable *draw)
+{
+   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
+   _EGLContext *ctx = _eglGetCurrentContext();
+
+   return ctx->Resource.Display == dri3_surf->base.Resource.Display;
+}
+
+static __DRIcontext *
+egl_dri3_get_dri_context(struct loader_dri3_drawable *draw)
+{
+   _EGLContext *ctx = _eglGetCurrentContext();
+   struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+
+   return dri2_ctx->dri_context;
+}
+
+static void
+egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags)
+{
+   struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
+   _EGLDisplay *disp = dri3_surf->base.Resource.Display;
+
+   dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->base);
+}
+
+static struct loader_dri3_vtable egl_dri3_vtable = {
+   .get_swap_interval = egl_dri3_get_swap_interval,
+   .clamp_swap_interval = egl_dri3_clamp_swap_interval,
+   .set_swap_interval = egl_dri3_set_swap_interval,
+   .set_drawable_size = egl_dri3_set_drawable_size,
+   .in_current_context = egl_dri3_in_current_context,
+   .get_dri_context = egl_dri3_get_dri_context,
+   .flush_drawable = egl_dri3_flush_drawable,
+   .show_fps = NULL,
+};
+
+static EGLBoolean
+dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+
+   (void) drv;
+
+   if (!_eglPutSurface(surf))
+      return EGL_TRUE;
+
+   loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
+
+   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);
+
+   loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
+
+   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;
+}
+
+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;
+   const __DRIconfig *dri_config;
+   xcb_drawable_t drawable;
+   xcb_screen_iterator_t s;
+   xcb_screen_t *screen;
+
+   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;
+      }
+
+      drawable = xcb_generate_id(dri2_dpy->conn);
+      xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
+                        drawable, screen->root,
+                        dri3_surf->base.Width, dri3_surf->base.Height);
+   }
+
+   dri_config = dri2_get_dri_config(dri2_conf, type,
+                                    dri3_surf->base.GLColorspace);
+
+   if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
+                                 dri2_dpy->dri_screen,
+                                 dri2_dpy->is_different_gpu, dri_config,
+                                 &dri2_dpy->loader_dri3_ext,
+                                 &egl_dri3_vtable,
+                                 &dri3_surf->loader_drawable)) {
+      _eglError(EGL_BAD_ALLOC, "dri3_surface_create");
+      goto cleanup_pixmap;
+   }
+
+   return &dri3_surf->base;
+
+ cleanup_pixmap:
+   if (type == EGL_PBUFFER_BIT)
+      xcb_free_pixmap(dri2_dpy->conn, 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);
+}
+
+static EGLBoolean
+dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface,
+                     EGLuint64KHR *ust, EGLuint64KHR *msc,
+                     EGLuint64KHR *sbc)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface);
+
+   return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0,
+                                   (int64_t *) ust, (int64_t *) msc,
+                                   (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE;
+}
+
+/**
+ * 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)
+{
+   /* There does not seem to be any kind of consensus on whether we should
+    * support front-buffer rendering or not:
+    * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html
+    */
+   _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
+   (void) driDrawable;
+   (void) loaderPrivate;
+}
+
+const __DRIimageLoaderExtension dri3_image_loader_extension = {
+   .base = { __DRI_IMAGE_LOADER, 1 },
+
+   .getBuffers          = loader_dri3_get_buffers,
+   .flushFrontBuffer    = dri3_flush_front_buffer,
+};
+
+static EGLBoolean
+dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw);
+
+   /* No-op for a pixmap or pbuffer surface */
+   if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT)
+      return 0;
+
+   return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable,
+                                       0, 0, 0, 0,
+                                       draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1;
+}
+
+static EGLBoolean
+dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf,
+                  void *native_pixmap_target)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+   xcb_pixmap_t target;
+
+   STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target));
+   target = (uintptr_t) native_pixmap_target;
+
+   loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target,
+                             dri3_surf->loader_drawable.drawable);
+
+   return EGL_TRUE;
+}
+
+static int
+dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+
+   return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable);
+}
+
+static __DRIdrawable *
+dri3_get_dri_drawable(_EGLSurface *surf)
+{
+   struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
+
+   return dri3_surf->loader_drawable.dri_drawable;
+}
+
+struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
+   .authenticate = NULL,
+   .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 = dri3_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,
+   .get_dri_drawable = dri3_get_dri_drawable,
+};
+
+static char *
+dri3_get_device_name(int fd)
+{
+   char *ret = NULL;
+
+   ret = drmGetRenderDeviceNameFromFd(fd);
+   if (ret)
+      return ret;
+
+   /* For dri3, render node support is required for WL_bind_wayland_display.
+    * In order not to regress on older systems without kernel or libdrm
+    * support, fall back to dri2. User can override it with environment
+    * variable if they don't need to use that extension.
+    */
+   if (getenv("EGL_FORCE_DRI3") == NULL) {
+      _eglLog(_EGL_WARNING, "Render node support not available, falling back to dri2");
+      _eglLog(_EGL_WARNING, "If you want to force dri3, set EGL_FORCE_DRI3 environment variable");
+   } else
+      ret = loader_get_device_name_for_fd(fd);
+
+   return ret;
+}
+
+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);
+
+   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(dri3_query);
+      free(error);
+      return EGL_FALSE;
+   }
+   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(present_query);
+      free(error);
+      return EGL_FALSE;
+   }
+   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 = loader_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 = dri3_get_device_name(dri2_dpy->fd);
+   if (!dri2_dpy->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 (file)
index 0000000..13d8572
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2015 Boyan Ding
+ *
+ * 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 "egl_dri2.h"
+
+_EGL_DRIVER_TYPECAST(dri3_egl_surface, _EGLSurface, obj)
+
+struct dri3_egl_surface {
+   _EGLSurface base;
+   struct loader_dri3_drawable loader_drawable;
+};
+
+extern const __DRIimageLoaderExtension dri3_image_loader_extension;
+extern struct dri2_egl_display_vtbl dri3_x11_display_vtbl;
+
+EGLBoolean
+dri3_x11_connect(struct dri2_egl_display *dri2_dpy);
+
+#endif