diff mbox

[8/8] Add DRI3+Present loader

Message ID 1383618208-21310-9-git-send-email-keithp@keithp.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Keith Packard Nov. 5, 2013, 2:23 a.m. UTC
Uses the __DRIimage loader interfaces.

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 configure.ac          |   12 +-
 src/glx/Makefile.am   |    2 +
 src/glx/dri3_common.c |  146 +++++
 src/glx/dri3_glx.c    | 1722 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/glx/dri3_priv.h   |  199 ++++++
 src/glx/glxclient.h   |    2 +
 src/glx/glxext.c      |    6 +-
 7 files changed, 2085 insertions(+), 4 deletions(-)
 create mode 100644 src/glx/dri3_common.c
 create mode 100644 src/glx/dri3_glx.c
 create mode 100644 src/glx/dri3_priv.h

Comments

Eric Anholt Nov. 5, 2013, 11:10 p.m. UTC | #1
I think I'm going to be griping about code duplication...

Keith Packard <keithp@keithp.com> writes:
> diff --git a/configure.ac b/configure.ac
> index 0a25047..074368c 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -38,6 +38,9 @@ LIBDRM_NVVIEUX_REQUIRED=2.4.33
>  LIBDRM_NOUVEAU_REQUIRED="2.4.33 libdrm >= 2.4.41"
>  LIBDRM_FREEDRENO_REQUIRED=2.4.39
>  DRI2PROTO_REQUIRED=2.6
> +DRI3PROTO_REQUIRED=1.0
> +PRESENTPROTO_REQUIRED=1.0
> +LIBUDEV_REQUIRED=151
>  GLPROTO_REQUIRED=1.4.14
>  LIBDRM_XORG_REQUIRED=2.4.24
>  LIBKMS_XORG_REQUIRED=1.0.0
> @@ -820,10 +823,13 @@ xyesno)
>          fi
>          PKG_CHECK_MODULES([DRI2PROTO], [dri2proto >= $DRI2PROTO_REQUIRED])
>          GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV libdrm >= $LIBDRM_REQUIRED"
> +        PKG_CHECK_MODULES([DRI3PROTO], [dri3proto >= $DRI3PROTO_REQUIRED])
> +        PKG_CHECK_MODULES([PRESENTPROTO], [presentproto >= $PRESENTPROTO_REQUIRED])
> +        PKG_CHECK_MODULES([LIBUDEV], [libudev >= $LIBUDEV_REQUIRED])
>      fi
>  
>      # find the DRI deps for libGL
> -    dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8"
> +    dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8 xcb-dri3 xcb-present xcb-sync xshmfence"

Patches need to land in XCB and get released before this can land.  I
don't even see patches on the xcb list yet.

> diff --git a/src/glx/dri3_common.c b/src/glx/dri3_common.c
> new file mode 100644
> index 0000000..c758f96
> --- /dev/null
> +++ b/src/glx/dri3_common.c
> @@ -0,0 +1,146 @@
> +/*
> + * 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.
> + */
> +
> +/*
> + * This code is derived from src/egl/drivers/dri2/common.c which
> + * carries the following copyright:
> + * 
> + * Copyright © 2011 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Kristian Høgsberg <krh@bitplanet.net>
> + *    Benjamin Franzke <benjaminfranzke@googlemail.com>
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <GL/gl.h>
> +#include "glapi.h"
> +#include "glxclient.h"
> +#include "xf86dri.h"
> +#include <dlfcn.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <sys/mman.h>
> +#include <sys/time.h>
> +#include "xf86drm.h"
> +#include "dri_common.h"
> +#include "dri3_priv.h"
> +
> +#define DRIVER_MAP_DRI3_ONLY

What does this define do?

> diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
> new file mode 100644
> index 0000000..4021baa
> --- /dev/null
> +++ b/src/glx/dri3_glx.c
> @@ -0,0 +1,1722 @@

> +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 inline Bool
> +dri3_fence_triggered(struct dri3_buffer *buffer) {
> +   return xshmfence_query(buffer->shm_fence);
> +}

'{' on a separate line, please.

> +static void
> +dri3_destroy_context(struct glx_context *context)
> +{
> +   struct dri3_context *pcp = (struct dri3_context *) context;
> +   struct dri3_screen *psc = (struct dri3_screen *) context->psc;
> +
> +   driReleaseDrawables(&pcp->base);
> +
> +   free((char *) context->extensions);
> +
> +   (*psc->core->destroyContext) (pcp->driContext);
> +
> +   free(pcp);
> +}
> +
> +static Bool
> +dri3_bind_context(struct glx_context *context, struct glx_context *old,
> +		  GLXDrawable draw, GLXDrawable read)
> +{
> +   struct dri3_context *pcp = (struct dri3_context *) context;
> +   struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc;
> +   struct dri3_drawable *pdraw, *pread;
> +
> +   pdraw = (struct dri3_drawable *) driFetchDrawable(context, draw);
> +   pread = (struct dri3_drawable *) driFetchDrawable(context, read);
> +
> +   driReleaseDrawables(&pcp->base);
> +
> +   if (pdraw == NULL || pread == NULL)
> +      return GLXBadDrawable;
> +
> +   if (!(*psc->core->bindContext) (pcp->driContext,
> +				   pdraw->driDrawable, pread->driDrawable))
> +      return GLXBadContext;
> +
> +   return Success;
> +}
> +
> +static void
> +dri3_unbind_context(struct glx_context *context, struct glx_context *new)
> +{
> +   struct dri3_context *pcp = (struct dri3_context *) context;
> +   struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc;
> +
> +   (*psc->core->unbindContext) (pcp->driContext);
> +}
> +
> +static struct glx_context *
> +dri3_create_context(struct glx_screen *base,
> +		    struct glx_config *config_base,
> +		    struct glx_context *shareList, int renderType)
> +{
> +   struct dri3_context *pcp, *pcp_shared;
> +   struct dri3_screen *psc = (struct dri3_screen *) base;
> +   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
> +   __DRIcontext *shared = NULL;
> +
> +   if (shareList) {
> +      /* If the shareList context is not a DRI3 context, we cannot possibly
> +       * create a DRI3 context that shares it.
> +       */
> +      if (shareList->vtable->destroy != dri3_destroy_context) {
> +	 return NULL;
> +      }
> +
> +      pcp_shared = (struct dri3_context *) shareList;
> +      shared = pcp_shared->driContext;
> +   }
> +
> +   pcp = calloc(1, sizeof *pcp);
> +   if (pcp == NULL)
> +      return NULL;
> +
> +   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
> +      free(pcp);
> +      return NULL;
> +   }
> +
> +   pcp->driContext =
> +      (*psc->image_driver->createNewContext) (psc->driScreen,
> +                                              config->driConfig, shared, pcp);
> +
> +   if (pcp->driContext == NULL) {
> +      free(pcp);
> +      return NULL;
> +   }
> +
> +   pcp->base.vtable = &dri3_context_vtable;
> +
> +   return &pcp->base;
> +}

This looks completely like dri2_create_context, except for missing
rendertype validation and a different calloc size.

> +static struct glx_context *
> +dri3_create_context_attribs(struct glx_screen *base,
> +			    struct glx_config *config_base,
> +			    struct glx_context *shareList,
> +			    unsigned num_attribs,
> +			    const uint32_t *attribs,
> +			    unsigned *error)
> +{
> +   struct dri3_context *pcp = NULL;
> +   struct dri3_context *pcp_shared = NULL;
> +   struct dri3_screen *psc = (struct dri3_screen *) base;
> +   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
> +   __DRIcontext *shared = NULL;
> +
> +   uint32_t minor_ver = 1;
> +   uint32_t major_ver = 2;
> +   uint32_t flags = 0;
> +   unsigned api;
> +   int reset = __DRI_CTX_RESET_NO_NOTIFICATION;
> +   uint32_t ctx_attribs[2 * 5];
> +   unsigned num_ctx_attribs = 0;
> +   uint32_t render_type;
> +
> +   /* Remap the GLX tokens to DRI2 tokens.
> +    */
> +   if (!dri2_convert_glx_attribs(num_attribs, attribs,
> +				 &major_ver, &minor_ver,

tabs :(

> +                                 &render_type, &flags, &api,
> +                                 &reset, error))
> +      goto error_exit;
> +
> +   /* Check the renderType value */
> +   if (!validate_renderType_against_config(config_base, render_type))
> +       goto error_exit;
> +
> +   if (shareList) {
> +      pcp_shared = (struct dri3_context *) shareList;
> +      shared = pcp_shared->driContext;
> +   }
> +
> +   pcp = calloc(1, sizeof *pcp);
> +   if (pcp == NULL) {
> +      *error = __DRI_CTX_ERROR_NO_MEMORY;
> +      goto error_exit;
> +   }
> +
> +   if (!glx_context_init(&pcp->base, &psc->base, &config->base))
> +      goto error_exit;
> +
> +   ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MAJOR_VERSION;
> +   ctx_attribs[num_ctx_attribs++] = major_ver;
> +   ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MINOR_VERSION;
> +   ctx_attribs[num_ctx_attribs++] = minor_ver;
> +
> +   /* Only send a value when the non-default value is requested.  By doing
> +    * this we don't have to check the driver's DRI3 version before sending the
> +    * default value.
> +    */
> +   if (reset != __DRI_CTX_RESET_NO_NOTIFICATION) {
> +      ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_RESET_STRATEGY;
> +      ctx_attribs[num_ctx_attribs++] = reset;
> +   }
> +
> +   if (flags != 0) {
> +      ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_FLAGS;
> +
> +      /* The current __DRI_CTX_FLAG_* values are identical to the
> +       * GLX_CONTEXT_*_BIT values.
> +       */
> +      ctx_attribs[num_ctx_attribs++] = flags;
> +   }
> +
> +   pcp->driContext =
> +      (*psc->image_driver->createContextAttribs) (psc->driScreen,
> +                                                  api,
> +                                                  config->driConfig,
> +                                                  shared,
> +                                                  num_ctx_attribs / 2,
> +                                                  ctx_attribs,
> +                                                  error,
> +                                                  pcp);
> +
> +   if (pcp->driContext == NULL)
> +      goto error_exit;
> +
> +   pcp->base.vtable = &dri3_context_vtable;
> +
> +   return &pcp->base;
> +
> +error_exit:
> +   free(pcp);
> +
> +   return NULL;
> +}

This looks like an exact copy of dri2_create_context_attribs except for
the vtable, the calloc size being different, the reset initialization,
and the createContextAttribs looking in image_driver instead of dri2.

This sucks.

> +
> +static __GLXDRIdrawable *
> +dri3_create_drawable(struct glx_screen *base, XID xDrawable,
> +                     GLXDrawable drawable, struct glx_config *config_base)
> +{
> +   struct dri3_drawable *pdraw;
> +   struct dri3_screen *psc = (struct dri3_screen *) base;
> +   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
> +   GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
> +
> +   pdraw = calloc(1, sizeof(*pdraw));
> +   if (!pdraw)
> +      return NULL;
> +
> +   pdraw->base.destroyDrawable = dri3_destroy_drawable;
> +   pdraw->base.xDrawable = xDrawable;
> +   pdraw->base.drawable = drawable;
> +   pdraw->base.psc = &psc->base;
> +//   pdraw->bufferCount = 0;

Leftover debug code?

> +   pdraw->swap_interval = 1; /* default may be overridden below */
> +   pdraw->have_back = 0;
> +   pdraw->have_fake_front = 0;
> +
> +   if (psc->config)
> +      psc->config->configQueryi(psc->driScreen,
> +				"vblank_mode", &vblank_mode);
> +
> +   switch (vblank_mode) {
> +   case DRI_CONF_VBLANK_NEVER:
> +   case DRI_CONF_VBLANK_DEF_INTERVAL_0:
> +      pdraw->swap_interval = 0;
> +      break;
> +   case DRI_CONF_VBLANK_DEF_INTERVAL_1:
> +   case DRI_CONF_VBLANK_ALWAYS_SYNC:
> +   default:
> +      pdraw->swap_interval = 1;
> +      break;
> +   }
> +
> +   (void) __glXInitialize(psc->base.dpy);
> +
> +   /* Create a new drawable */
> +   pdraw->driDrawable =
> +      (*psc->image_driver->createNewDrawable) (psc->driScreen,
> +                                               config->driConfig, pdraw);
> +
> +   if (!pdraw->driDrawable) {
> +      free(pdraw);
> +      return NULL;
> +   }
> +
> +   /*
> +    * Make sure server has the same swap interval we do for the new
> +    * drawable.
> +    */
> +   if (psc->vtable.setSwapInterval)
> +      psc->vtable.setSwapInterval(&pdraw->base, pdraw->swap_interval);
> +
> +   return &pdraw->base;
> +}

Finally, a function different enough that I think it merits being a new
implementation :)

> +static int
> +dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
> +                  int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc)
> +{
> +   xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
> +   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
> +   xcb_generic_event_t *ev;
> +   xcb_present_generic_event_t *ge;
> +
> +   /* Ask for the an event for the target MSC */
> +   ++priv->present_msc_request_serial;
> +   xcb_present_notify_msc(c,
> +                          priv->base.xDrawable,
> +                          priv->present_msc_request_serial,
> +                          target_msc,
> +                          divisor,
> +                          remainder);
> +
> +   xcb_flush(c);
> +
> +   /* Wait for the event */
> +   if (priv->special_event) {
> +      while (priv->present_msc_request_serial != priv->present_msc_event_serial) {
> +         ev = xcb_wait_for_special_event(c, priv->special_event);
> +         if (!ev)
> +            break;
> +         ge = (void *) ev;
> +         present_handle_special_event(priv, ge);
> +      }
> +   }
> +
> +   *ust = priv->ust;
> +   *msc = priv->msc;
> +

funny extra newline.

> +   *sbc = priv->sbc;
> +
> +   return 1;
> +}

> +static int
> +dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust,
> +                  int64_t *msc, int64_t *sbc)
> +{
> +   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
> +
> +   while (priv->sbc < target_sbc) {
> +      sleep(1);
> +   }

Some sort of comment about what's going on here?  Seems like sleep(1)
would always be a wrong thing to execute.

> +   return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc, sbc);
> +}


> +/**
> + * dri3Throttle - Request driver throttling
> + *
> + * This function uses the DRI2 throttle extension to give the
> + * driver the opportunity to throttle on flush front, copysubbuffer
> + * and swapbuffers.
> + */
> +static void
> +dri3_throttle(struct dri3_screen *psc,
> +              struct dri3_drawable *draw,
> +              enum __DRI2throttleReason reason)
> +{
> +   if (psc->throttle) {
> +      __DRIcontext *ctx = dri3_get_current_context();
> +
> +      psc->throttle->throttle(ctx, draw->driDrawable, reason);
> +   }
> +}

I think we can drop this entirely thanks to flush_with_flags (see
below).  The gallium-only implementation of this driver extension is
just a call to flush_with_flags.

> +
> +/**
> + * 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 dri3_screen *psc,
> +           __DRIcontext *ctx,
> +           struct dri3_drawable *draw,
> +           unsigned flags,
> +           enum __DRI2throttleReason throttle_reason)
> +{
> +   if (ctx && psc->f && psc->f->base.version >= 4) {
> +      psc->f->flush_with_flags(ctx, draw->driDrawable, flags, throttle_reason);
> +   } else {
> +      if (flags & __DRI2_FLUSH_CONTEXT)
> +         glFlush();
> +
> +      if (psc->f)
> +         psc->f->flush(draw->driDrawable);
> +
> +      dri3_throttle(psc, draw, throttle_reason);
> +   }
> +}

I'd rather insist that the driver supports flush_with_flags if you do DRI3.

> +static void
> +dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
> +		  int width, int height, Bool flush)
> +{
> +   _dri3_copy_sub_buffer(pdraw, x, y, width, height,
> +                         __DRI2_THROTTLE_COPYSUBBUFFER, flush);
> +}

This appears to be a pointless wrapper.

> +static void
> +dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src)
> +{
> +   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
> +   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
> +
> +   if (psc->f)
> +      (*psc->f->flush) (priv->driDrawable);

Use flush_with_flags instead.

> +   dri3_copy_area(c,
> +                  src, dest,
> +                  dri3_drawable_gc(priv),
> +                  0, 0, 0, 0, priv->width, priv->height);
> +}

DRI2CopyRegion round-tripped, while this call doesn't.  As a result, I
think dri3_wait_x is broken because it doesn't ensure that the copyarea
actually happens before your driver goes rendering again.  dri3_wait_gl
may be similarly wrong in the other way.

We don't have testing for glXWaitGL() or glXWaitX() at all, and that's
bad.

> +static void
> +dri3_wait_x(struct glx_context *gc)
> +{
> +   struct dri3_drawable *priv = (struct dri3_drawable *)
> +      GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
> +
> +   if (priv == NULL || !priv->have_fake_front)
> +      return;
> +
> +   dri3_copy_drawable(priv, dri3_fake_front_buffer(priv)->pixmap, priv->base.xDrawable);
> +}
> +
> +static void
> +dri3_wait_gl(struct glx_context *gc)
> +{
> +   struct dri3_drawable *priv = (struct dri3_drawable *)
> +      GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
> +
> +   if (priv == NULL || !priv->have_fake_front)
> +      return;
> +
> +   dri3_copy_drawable(priv, priv->base.xDrawable, dri3_fake_front_buffer(priv)->pixmap);
> +}

What's going on with fence reset/triggering being present in
copysubbuffer but not these entrypoints?

> +static struct dri3_buffer *
> +dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, unsigned int format, int width, int height, int depth)

80-column wrap

> +{
> +   struct dri3_screen *psc = (struct dri3_screen *) glx_screen;
> +   Display *dpy = glx_screen->dpy;
> +   struct dri3_buffer *buffer;
> +   xcb_connection_t *c = XGetXCBConnection(dpy);
> +   xcb_pixmap_t pixmap;
> +   xcb_sync_fence_t sync_fence;
> +   int32_t *shm_fence;
> +   int buffer_fd, fence_fd;
> +   int stride;
> +
> +   fence_fd = xshmfence_alloc_shm();
> +   if (fence_fd < 0)
> +      return NULL;
> +   shm_fence = xshmfence_map_shm(fence_fd);
> +   if (shm_fence == NULL)
> +      goto no_shm_fence;
> +
> +   buffer = calloc(1, sizeof (struct dri3_buffer));
> +   if (!buffer)
> +      goto no_buffer;
> +
> +   buffer->image = (*psc->image->createImage) (psc->driScreen,
> +                                               width, height,
> +                                               format,
> +                                               __DRI_IMAGE_USE_SHARE|__DRI_IMAGE_USE_SCANOUT,
> +                                               buffer);
> +
> +   

trailing whitespace

> +   if (!buffer->image)
> +      goto no_image;
> +
> +   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_STRIDE, &stride))
> +      goto no_buffer_attrib;
> +
> +   buffer->pitch = stride;
> +
> +   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_FD, &buffer_fd))
> +      goto no_buffer_attrib;


> +
> +   xcb_dri3_pixmap_from_buffer(c,
> +                               (pixmap = xcb_generate_id(c)),
> +                               draw,
> +                               buffer->size,
> +                               width, height, buffer->pitch,
> +                               depth, buffer->cpp * 8,

I don't see buffer->cpp initialized anywhere.

> +   /* Mark the buffer as idle */
> +   dri3_fence_set(buffer);
> +
> +   return buffer;
> +   

trailing whitespace

> +static void
> +dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer)
> +{
> +   struct dri3_screen   *psc = (struct dri3_screen *) pdraw->base.psc;
> +   xcb_connection_t     *c = XGetXCBConnection(pdraw->base.psc->dpy);
> +
> +   xcb_free_pixmap(c, buffer->pixmap);
> +   xcb_sync_destroy_fence(c, buffer->sync_fence);
> +   xshmfence_unmap_shm(buffer->shm_fence);
> +   (*psc->image->destroyImage)(buffer->image);
> +   free(buffer);
> +}
> +
> +
> +
> +static void
> +present_flush_events(struct dri3_drawable *priv)
> +{
> +   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
> +
> +   /* Check to see if any configuration changes have occurred
> +    * since we were last invoked
> +    */
> +   if (priv->special_event) {
> +      xcb_generic_event_t    *ev;
> +
> +      while ((ev = xcb_check_for_special_event(c, priv->special_event)) != NULL) {
> +         xcb_present_generic_event_t *ge = (void *) ev;
> +         present_handle_special_event(priv, ge);
> +      }
> +   }
> +}
> +
> +static int
> +dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
> +{
> +   struct dri3_drawable *priv = loaderPrivate;
> +   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
> +
> +   /* First time through, go get the current drawable geometry
> +    */
> +   if (priv->width == 0 || priv->height == 0 || priv->depth == 0) {
> +      xcb_get_geometry_cookie_t                 geom_cookie;
> +      xcb_get_geometry_reply_t                  *geom_reply;
> +      xcb_void_cookie_t                         cookie;
> +      xcb_generic_error_t                       *error;
> +
> +      cookie = xcb_present_select_input_checked(c,
> +                                                (priv->eid = xcb_generate_id(c)),
> +                                                priv->base.xDrawable,
> +                                                XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
> +                                                XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
> +                                                XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
> +         
> +      if (!priv->present_extension) {
> +         priv->present_extension = xcb_get_extension_data(c, &xcb_present_id);
> +         if (!priv->present_extension)
> +            return false;
> +      }
> +
> +      priv->special_event = xcb_register_for_special_event(c,
> +                                                           priv->present_extension->major_opcode,
> +                                                           priv->eid,
> +                                                           priv->stamp);
> +
> +      geom_cookie = xcb_get_geometry(c, priv->base.xDrawable);
> +
> +      geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL);
> +
> +      if (!geom_reply)
> +         return false;
> +
> +      priv->width = geom_reply->width;
> +      priv->height = geom_reply->height;
> +      priv->depth = geom_reply->depth;
> +      priv->is_pixmap = false;
> +
> +      free(geom_reply);
> +
> +      error = xcb_request_check(c, cookie);
> +
> +      if (error) {
> +         if (error->error_code != BadWindow) {
> +            free(error);
> +            return false;
> +         }
> +         priv->is_pixmap = true;
> +         xcb_unregister_for_special_event(c, priv->special_event);
> +         priv->special_event = NULL;
> +      }
> +   }

You should probably comment what's going on here.  Is an error going to
be returned iff it's a pixmap?

> +   

trailing whitespace.

> +static int
> +image_format_to_fourcc(int format)
> +{
> +
> +   /* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */
> +   switch (format) {
> +   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;
> +//   case __DRI_IMAGE_FORMAT_R8: return __DRI_IMAGE_FOURCC_R8;
> +//   case __DRI_IMAGE_FORMAT_GR88: return __DRI_IMAGE_FOURCC_GR88;
> +//   case __DRI_IMAGE_FORMAT_NONE: return __DRI_IMAGE_FOURCC_NONE;
> +//   case __DRI_IMAGE_FORMAT_XRGB2101010: return __DRI_IMAGE_FOURCC_XRGB2101010;
> +//   case __DRI_IMAGE_FORMAT_ARGB2101010: return __DRI_IMAGE_FOURCC_ARGB2101010;

What's up with commented out formats?

> +   }
> +   return 0;
> +}
> +
> +static struct dri3_buffer *
> +dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
> +                       unsigned int format,
> +                       enum dri3_buffer_type buffer_type,
> +                       void *loaderPrivate)
> +{
> +   struct dri3_drawable                 *pdraw = loaderPrivate;
> +   int                                  buf_id = buffer_type == dri3_pixmap_buf_id(buffer_type);
> +   struct dri3_buffer                   *buffer = pdraw->buffers[buf_id];
> +   Pixmap                               pixmap;
> +   xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
> +   xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
> +   int                                  *fds;
> +   int                                  buffer_fd;
> +   Display                              *dpy;
> +   struct dri3_screen                   *psc;
> +   xcb_connection_t                     *c;
> +   xcb_sync_fence_t                     sync_fence;
> +   int32_t                              *shm_fence;
> +   int                                  fence_fd;
> +   __DRIimage                           *image_planar;
> +   int                                  stride, offset;
> +
> +   if (buffer)
> +      return buffer;
> +
> +   pixmap = pdraw->base.xDrawable;
> +   psc = (struct dri3_screen *) pdraw->base.psc;
> +   dpy = psc->base.dpy;
> +   c = XGetXCBConnection(dpy);
> +
> +   buffer = calloc(1, sizeof (struct dri3_buffer));
> +   if (!buffer)
> +      goto no_buffer;

> +
> +   image_planar = (*psc->image->createImageFromFds) (psc->driScreen,
> +                                                     bp_reply->width,
> +                                                     bp_reply->height,
> +                                                     image_format_to_fourcc(format),
> +                                                     fds, 1,
> +                                                     &stride, &offset, buffer);
> +   close(buffer_fd);

just drop buffer_fd and reference fds[0] again?

> +   if (!image_planar)
> +      goto no_image;
> +
> +   buffer->image = (*psc->image->fromPlanar)(image_planar, 0, buffer);
> +
> +   (*psc->image->destroyImage)(image_planar);

Is the fromPlanar step here actually needed?  It looks like since
num_fds == 1 you get a functional image from the first step.

> +static int
> +dri3_get_buffers(__DRIdrawable *driDrawable,
> +                 int *width, int *height,
> +                 unsigned int format,
> +                 uint32_t *stamp,
> +                 void *loaderPrivate,
> +                 uint32_t buffer_mask,
> +                 struct __DRIimageList *buffers)
> +{
> +   struct dri3_drawable *priv = loaderPrivate;
> +   struct dri3_buffer   *front, *back;
> +
> +   buffers->front = NULL;
> +   buffers->back = NULL;
> +
> +   front = NULL;
> +   back = NULL;
> +
> +   if (!dri3_update_drawable(driDrawable, loaderPrivate))
> +      return false;
> +
> +   if (priv->is_pixmap)
> +      buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
> +
> +   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
> +      if (priv->is_pixmap)
> +         front = dri3_get_pixmap_buffer(driDrawable,
> +                                        format,
> +                                        dri3_buffer_front,
> +                                        loaderPrivate);
> +      else
> +         front = dri3_get_buffer(driDrawable,
> +                                 format,
> +                                 dri3_buffer_front,
> +                                 loaderPrivate);
> +
> +      if (!front)
> +         return false;
> +      priv->have_fake_front = !priv->is_pixmap;
> +   } else {
> +      dri3_free_buffers(driDrawable, dri3_buffer_front, loaderPrivate);
> +      priv->have_fake_front = 0;
> +   }
> +
> +   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
> +      back = dri3_get_buffer(driDrawable,
> +                             format,
> +                             dri3_buffer_back,
> +                             loaderPrivate);
> +      if (!back)
> +         return false;

I think this early return leaks front.  Also have_fake_front's setting
leaked in even though we're not updating priv->front, should it have?

> +      priv->have_back = 1;
> +   } else {
> +      dri3_free_buffers(driDrawable, dri3_buffer_back, loaderPrivate);
> +      priv->have_back = 0;
> +   }
> +
> +   if (front)
> +      buffers->front = front->image;
> +   
> +   if (back)
> +      buffers->back = back->image;
> +
> +   priv->stamp = stamp;
> +
> +   /* Report back current geometry */
> +   *width = priv->width;
> +   *height = priv->height;
> +   return true;
> +}
> +

> +
> +static int
> +dri3_query_version(Display *dpy, int *major, int *minor)
> +{
> +   xcb_dri3_query_version_cookie_t      cookie;
> +   xcb_dri3_query_version_reply_t       *reply;       

trailing whitespace

> +   xcb_connection_t                     *c = XGetXCBConnection(dpy);
> +   xcb_generic_error_t                  *error;
> +
> +   cookie = xcb_dri3_query_version(c,
> +                                   XCB_DRI3_MAJOR_VERSION,
> +                                   XCB_DRI3_MINOR_VERSION);
> +   reply = xcb_dri3_query_version_reply(c, cookie, &error);
> +   if (!reply) {
> +      if (error) {
> +         free(error);
> +      }

No need for NULL-checking free().

Same 2 comments apply to the next 2 functions.

> +      return 0;
> +   }
> +   *major = reply->major_version;
> +   *minor = reply->minor_version;
> +   free(reply);
> +   return 1;
> +}

> +static const __DRIimageLoaderExtension imageLoaderExtension = {
> +   {__DRI_IMAGE_LOADER, __DRI_IMAGE_LOADER_VERSION},
> +   .getBuffers = dri3_get_buffers,
> +   .flushFrontBuffer = dri3_flush_front_buffer,
> +};
> +
> +static void
> +dri3_bind_tex_image(Display * dpy,
> +		    GLXDrawable drawable,
> +		    int buffer, const int *attrib_list)
> +{
> +   struct glx_context *gc = __glXGetCurrentContext();
> +   struct dri3_context *pcp = (struct dri3_context *) gc;
> +   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
> +   struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
> +   struct dri3_screen *psc;
> +
> +   if (pdraw != NULL) {
> +      psc = (struct dri3_screen *) base->psc;
> +
> +      if (psc->f &&
> +           psc->f->base.version >= 3 && psc->f->invalidate)
> +	 psc->f->invalidate(pdraw->driDrawable);
> +
> +      XSync(dpy, false);
> +      if (psc->texBuffer->base.version >= 2 &&
> +	  psc->texBuffer->setTexBuffer2 != NULL) {
> +	 (*psc->texBuffer->setTexBuffer2) (pcp->driContext,
> +					   pdraw->base.textureTarget,
> +					   pdraw->base.textureFormat,
> +					   pdraw->driDrawable);
> +      }
> +      else {
> +	 (*psc->texBuffer->setTexBuffer) (pcp->driContext,
> +					  pdraw->base.textureTarget,
> +					  pdraw->driDrawable);
> +      }
> +   }
> +}

Tab indentation :(

I'd really like to see less loader code duplication.  But if you have
to, at least don't support old setTexBuffer when you know the driver's
new enough that it's got DRI3.

> +static void
> +dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
> +{
> +#if __DRI_TEX_BUFFER_VERSION >= 3
> +   struct glx_context *gc = __glXGetCurrentContext();
> +   struct dri3_context *pcp = (struct dri3_context *) gc;
> +   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
> +   struct glx_display *dpyPriv = __glXInitialize(dpy);
> +   struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
> +   struct dri3_display *pdp =
> +      (struct dri3_display *) dpyPriv->dri3Display;
> +   struct dri3_screen *psc;
> +
> +   if (pdraw != NULL) {
> +      psc = (struct dri3_screen *) base->psc;
> +
> +      if (psc->texBuffer->base.version >= 3 &&
> +          psc->texBuffer->releaseTexBuffer != NULL) {
> +         (*psc->texBuffer->releaseTexBuffer) (pcp->driContext,
> +                                           pdraw->base.textureTarget,
> +                                           pdraw->driDrawable);
> +      }
> +   }
> +#endif

Remove the #ifdef.  You're in the tree, you know its value.

> +}
> +
> +static const struct glx_context_vtable dri3_context_vtable = {
> +   dri3_destroy_context,
> +   dri3_bind_context,
> +   dri3_unbind_context,
> +   dri3_wait_gl,
> +   dri3_wait_x,
> +   DRI_glXUseXFont,
> +   dri3_bind_tex_image,
> +   dri3_release_tex_image,
> +   NULL, /* get_proc_address */
> +};
> +
> +static void
> +dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
> +                     const char *driverName)
> +{
> +//   const struct dri3_display *const pdp = (struct dri3_display *) priv->dri3Display;

more commented leftovers.

> +   const __DRIextension **extensions;
> +   unsigned mask;
> +   int i;
> +
> +   extensions = psc->core->getExtensions(psc->driScreen);
> +
> +   __glXEnableDirectExtension(&psc->base, "GLX_SGI_video_sync");
> +   __glXEnableDirectExtension(&psc->base, "GLX_SGI_swap_control");
> +   __glXEnableDirectExtension(&psc->base, "GLX_MESA_swap_control");
> +   __glXEnableDirectExtension(&psc->base, "GLX_SGI_make_current_read");
> +
> +   /*
> +    * GLX_INTEL_swap_event is broken on the server side, where it's
> +    * currently unconditionally enabled. This completely breaks
> +    * systems running on drivers which don't support that extension.
> +    * There's no way to test for its presence on this side, so instead
> +    * of disabling it unconditionally, just disable it for drivers
> +    * which are known to not support it, or for DDX drivers supporting
> +    * only an older (pre-ScheduleSwap) version of DRI2.
> +    *
> +    * This is a hack which is required until:
> +    * http://lists.x.org/archives/xorg-devel/2013-February/035449.html
> +    * is merged and updated xserver makes it's way into distros:
> +    */
> +//   if (pdp->swapAvailable && strcmp(driverName, "vmwgfx") != 0) {
> +//      __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event");
> +//   }

more commented leftovers.  Are you dropping swap_event support?

> +
> +   mask = psc->image_driver->getAPIMask(psc->driScreen);
> +
> +   __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context");
> +   __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context_profile");
> +
> +   if ((mask & (1 << __DRI_API_GLES2)) != 0)
> +      __glXEnableDirectExtension(&psc->base,
> +                                 "GLX_EXT_create_context_es2_profile");
> +
> +   for (i = 0; extensions[i]; i++) {
> +      if ((strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
> +	 psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
> +	 __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
> +      }
> +
> +      if ((strcmp(extensions[i]->name, __DRI2_FLUSH) == 0)) {
> +	 psc->f = (__DRI2flushExtension *) extensions[i];
> +	 /* internal driver extension, no GL extension exposed */
> +      }
> +
> +      if ((strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0))
> +	 psc->config = (__DRI2configQueryExtension *) extensions[i];
> +
> +      if (((strcmp(extensions[i]->name, __DRI2_THROTTLE) == 0)))
> +	 psc->throttle = (__DRI2throttleExtension *) extensions[i];
> +
> +      if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0)
> +         __glXEnableDirectExtension(&psc->base,
> +                                    "GLX_ARB_create_context_robustness");
> +   }
> +}
> +

This is horribly duplicated with dri2, but that's also horribly
duplicated with EGL's dri3.  I really think we need to do a
dri_loader_common between all of them with a bunch of this crap.

> +   tmp = getenv("LIBGL_SHOW_FPS");
> +   psc->show_fps = tmp && strcmp(tmp, "1") == 0;

Dead code.

> +/*
> + * Allocate, initialize and return a __DRIdisplayPrivate object.
> + * This is called from __glXInitialize() when we are given a new
> + * display pointer.
> + */
> +_X_HIDDEN __GLXDRIdisplay *
> +dri3_create_display(Display * dpy)
> +{
> +   struct dri3_display *pdp;
> +   int i;
> +
> +   pdp = malloc(sizeof *pdp);
> +   if (pdp == NULL)
> +      return NULL;
> +
> +   if (!dri3_query_version(dpy, &pdp->dri3Major, &pdp->dri3Minor))
> +      goto no_extension;
> +
> +   if (!present_query_version(dpy, &pdp->presentMajor, &pdp->presentMinor))
> +      goto no_extension;
> +
> +   pdp->base.destroyDisplay = dri3_destroy_display;
> +   pdp->base.createScreen = dri3_create_screen;
> +
> +   i = 0;
> +
> +   pdp->loader_extensions[i++] = &imageLoaderExtension.base;
> +   

trailing whitespace

> diff --git a/src/glx/dri3_priv.h b/src/glx/dri3_priv.h
> new file mode 100644
> index 0000000..2873919
> --- /dev/null
> +++ b/src/glx/dri3_priv.h

> +
> +struct dri3_buffer {
> +   __DRIimage   *image;
> +   uint32_t     pixmap;

> +   uint32_t     sync_fence;
> +   int32_t      *shm_fence;
> +   GLboolean    busy;

Can we get some comments on what these 3 fields do?  These
synchronization details were a huge part of the dri3 discussions, and I
know you've gone several ways about things in the process of
development, but I see just a single comment about what fences do:

 +   /* Mark the buffer as idle */

That's... not enough.

> +struct dri3_drawable
> +{

> +   /* For WaitMSC */
> +   uint32_t     present_msc_request_serial;
> +   uint32_t     present_msc_event_serial;
> +   

whitespace
Keith Packard Nov. 6, 2013, 2:32 a.m. UTC | #2
Eric Anholt <eric@anholt.net> writes:

I've pushed a patch responding to these comments to my dri3 branch and
will send that out shortly. I will merge those changes with the original
DRI3+Present loader patch so that there is only one commit when the
review process is complete.

> I think I'm going to be griping about code duplication...

Yeah, I'm really not sure what to do about that. The alternative would
be to refactor the DRI2 backend and then land the DRI3 backend on top of
some shared code, but frankly I'm not that excited about breaking DRI2.

Do you want me to actually go ahead and try to do that?

> Patches need to land in XCB and get released before this can land.  I
> don't even see patches on the xcb list yet.

I sent out the patches to the xorg-devel list today after reworking and
cleaning them up. I'd forgotten that there was a separate xcb list.

>> +
>> +#define DRIVER_MAP_DRI3_ONLY
>
> What does this define do?

It is designed to be used in case there are two drivers for a chipset,
one DRI2 one and one DRI3 one. Much like the DRIVER_MAP_GALLIUM_ONLY
flag. It's not currently being used at all, so it should probably just
get removed until necessary.

> '{' on a separate line, please.

Fixed.

> This looks completely like dri2_create_context, except for missing
> rendertype validation and a different calloc size.

Yup. It's cult-n-paste. Note that the license at the top of the file
tries to make it clear that *lots* of the code in this file was copied
From the DRI2 versions. They all take new data structures now, with the
DRI2 stuff replaced with DRI3 stuff, so we can't just shared the same
binaries without a bunch of rework.

> tabs :(

Oh, looks like my xorg emacs settings for using spaces only aren't
applying to mesa. Sorry. And fixed.

> This looks like an exact copy of dri2_create_context_attribs except for
> the vtable, the calloc size being different, the reset initialization,
> and the createContextAttribs looking in image_driver instead of dri2.
>
> This sucks.

As above.

>> +//   pdraw->bufferCount = 0;
>
> Leftover debug code?

Already removed.

>> +
>> +   *ust = priv->ust;
>> +   *msc = priv->msc;
>> +
>
> funny extra newline.

Fixed.

> Some sort of comment about what's going on here?  Seems like sleep(1)
> would always be a wrong thing to execute.

I've added this comment:

/** dri3_wait_for_sbc
 *
 * Wait for the swap buffer count to increase. The only way this
 * can happen is if some other thread is doing swap buffers as
 * we no longer share swap buffer counts with other processes.
 *
 * I'm not sure this is actually useful as such, and so this
 * implementation is a kludge that just polls once a second
 */

I'm not sure what else to do here; presumably there would be locking
preventing multiple threads from getting into this code at the same
time. As none of this code is thread-safe, I'm pretty sure this would
never work anyways.

> I think we can drop this entirely thanks to flush_with_flags (see
> below).  The gallium-only implementation of this driver extension is
> just a call to flush_with_flags.

> I'd rather insist that the driver supports flush_with_flags if you do
> DRI3.

Done.

>
>> +static void
>> +dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
>> +		  int width, int height, Bool flush)
>> +{
>> +   _dri3_copy_sub_buffer(pdraw, x, y, width, height,
>> +                         __DRI2_THROTTLE_COPYSUBBUFFER, flush);
>> +}
>
> This appears to be a pointless wrapper.

Removed.

>> +static void
>> +dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src)
>> +{
>> +   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
>> +   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
>> +
>> +   if (psc->f)
>> +      (*psc->f->flush) (priv->driDrawable);
>
> Use flush_with_flags instead.

I call dri3_flush. I've also removed the context argument to dri3_flush
as everyone was passing dri3_current_context, then removed
dri3_current_context and folded that code into dri3_flush.

>> +   dri3_copy_area(c,
>> +                  src, dest,
>> +                  dri3_drawable_gc(priv),
>> +                  0, 0, 0, 0, priv->width, priv->height);
>> +}
>
> DRI2CopyRegion round-tripped, while this call doesn't.  As a result, I
> think dri3_wait_x is broken because it doesn't ensure that the copyarea
> actually happens before your driver goes rendering again.  dri3_wait_gl
> may be similarly wrong in the other way.
>
> We don't have testing for glXWaitGL() or glXWaitX() at all, and that's
> bad.
...
> What's going on with fence reset/triggering being present in
> copysubbuffer but not these entrypoints?

Good catch. I've added fencing around the dri3_copy_area call to
synchronize with the X server.

>> +static struct dri3_buffer *
>> +dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, unsigned int format, int width, int height, int depth)
>
> 80-column wrap

Fixed.

> trailing whitespace

Fixed (everywhere)

>
>> +   if (!buffer->image)
>> +      goto no_image;
>> +
>> +   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_STRIDE, &stride))
>> +      goto no_buffer_attrib;
>> +
>> +   buffer->pitch = stride;
>> +
>> +   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_FD, &buffer_fd))
>> +      goto no_buffer_attrib;
>
>
>> +
>> +   xcb_dri3_pixmap_from_buffer(c,
>> +                               (pixmap = xcb_generate_id(c)),
>> +                               draw,
>> +                               buffer->size,
>> +                               width, height, buffer->pitch,
>> +                               depth, buffer->cpp * 8,
>
> I don't see buffer->cpp initialized anywhere.

Ick. Lost this in the conversion to __DRIimage. I've added a function
that computes cpp from the __DRI_IMAGE_FORMAT_* value and uses that.

> You should probably comment what's going on here.  Is an error going to
> be returned iff it's a pixmap?

Comments added (the select_input request returns a BadWindow error for
pixmaps, which this code interprets as 'that must be a pixmap')

> What's up with commented out formats?

They're just missing from the __DRI_IMAGE_FOURCC definitions. I've just
removed them for now. Krh promises to eliminate all of the
__DRI_IMAGE_FORMAT_* bits and use __DRI_IMAGE_FOURCC everywhere at some
point -- that will eliminate this function entirely.

>> +
>> +   image_planar = (*psc->image->createImageFromFds) (psc->driScreen,
>> +                                                     bp_reply->width,
>> +                                                     bp_reply->height,
>> +                                                     image_format_to_fourcc(format),
>> +                                                     fds, 1,
>> +                                                     &stride, &offset, buffer);
>> +   close(buffer_fd);
>
> just drop buffer_fd and reference fds[0] again?

Done.

>> +   if (!image_planar)
>> +      goto no_image;
>> +
>> +   buffer->image = (*psc->image->fromPlanar)(image_planar, 0, buffer);
>> +
>> +   (*psc->image->destroyImage)(image_planar);
>
> Is the fromPlanar step here actually needed?  It looks like since
> num_fds == 1 you get a functional image from the first step.

Kristian says that it is required -- the upper level __DRIimage might be
a wrapper that points at multiple __DRIimage objects, and in this case
it would point at precisely one of them. I note that the intel driver
doesn't work like that though.

> I think this early return leaks front.  Also have_fake_front's setting
> leaked in even though we're not updating priv->front, should it have?

It isn't "leaked", in that the dri3 code keeps track of the allocated
fake front and will re-use it in future allocations, and free it when
the drawable is destroyed.

However, you're right in asserting that the have_fake_front shouldn't be
set to TRUE until we are going to return success. I've moved that down.

> No need for NULL-checking free().

Fixed

> I'd really like to see less loader code duplication.  But if you have
> to, at least don't support old setTexBuffer when you know the driver's
> new enough that it's got DRI3.

Done. The DRI3 code now requires version 3 of the texBuffer extension
with setTexBuffer2 and releaseTexBuffer defined.

> Remove the #ifdef.  You're in the tree, you know its value.

Removed.

>> +//   if (pdp->swapAvailable && strcmp(driverName, "vmwgfx") != 0) {
>> +//      __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event");
>> +//   }
>
> more commented leftovers.  Are you dropping swap_event support?

Yes, DRI3/Present does not support GLX_INTEL_swap_event. I'd have to add
X server support to deliver that event. Is it useful enough that I
should do this?

> This is horribly duplicated with dri2, but that's also horribly
> duplicated with EGL's dri3.  I really think we need to do a
> dri_loader_common between all of them with a bunch of this crap.

I'm not sure where we would put code to share code between glx and egl,
but it sure would be nice.

>> +   tmp = getenv("LIBGL_SHOW_FPS");
>> +   psc->show_fps = tmp && strcmp(tmp, "1") == 0;
>
> Dead code.

Removed.

>> +
>> +struct dri3_buffer {
>> +   __DRIimage   *image;
>> +   uint32_t     pixmap;
>
>> +   uint32_t     sync_fence;
>> +   int32_t      *shm_fence;
>> +   GLboolean    busy;
>
> Can we get some comments on what these 3 fields do?  These
> synchronization details were a huge part of the dri3 discussions, and I
> know you've gone several ways about things in the process of
> development, but I see just a single comment about what fences do:

   /* 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 */
   int32_t      *shm_fence;     /* pointer to xshmfence object */
   GLboolean    busy;           /* Set on swap, cleared on IdleNotify */

>  +   /* Mark the buffer as idle */
>
> That's... not enough.

This is at allocation time; we know the buffer is idle. What else would
you like me to do here?

>
>> +struct dri3_drawable
>> +{
>
>> +   /* For WaitMSC */
>> +   uint32_t     present_msc_request_serial;
>> +   uint32_t     present_msc_event_serial;
>> +   
>
> whitespace

Fixed.
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 0a25047..074368c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,6 +38,9 @@  LIBDRM_NVVIEUX_REQUIRED=2.4.33
 LIBDRM_NOUVEAU_REQUIRED="2.4.33 libdrm >= 2.4.41"
 LIBDRM_FREEDRENO_REQUIRED=2.4.39
 DRI2PROTO_REQUIRED=2.6
+DRI3PROTO_REQUIRED=1.0
+PRESENTPROTO_REQUIRED=1.0
+LIBUDEV_REQUIRED=151
 GLPROTO_REQUIRED=1.4.14
 LIBDRM_XORG_REQUIRED=2.4.24
 LIBKMS_XORG_REQUIRED=1.0.0
@@ -820,10 +823,13 @@  xyesno)
         fi
         PKG_CHECK_MODULES([DRI2PROTO], [dri2proto >= $DRI2PROTO_REQUIRED])
         GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV libdrm >= $LIBDRM_REQUIRED"
+        PKG_CHECK_MODULES([DRI3PROTO], [dri3proto >= $DRI3PROTO_REQUIRED])
+        PKG_CHECK_MODULES([PRESENTPROTO], [presentproto >= $PRESENTPROTO_REQUIRED])
+        PKG_CHECK_MODULES([LIBUDEV], [libudev >= $LIBUDEV_REQUIRED])
     fi
 
     # find the DRI deps for libGL
-    dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8"
+    dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8 xcb-dri3 xcb-present xcb-sync xshmfence"
 
     # add xf86vidmode if available
     PKG_CHECK_MODULES([XF86VIDMODE], [xxf86vm], HAVE_XF86VIDMODE=yes, HAVE_XF86VIDMODE=no)
@@ -833,8 +839,8 @@  xyesno)
 
     PKG_CHECK_MODULES([DRIGL], [$dri_modules])
     GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV $dri_modules"
-    X11_INCLUDES="$X11_INCLUDES $DRIGL_CFLAGS"
-    GL_LIB_DEPS="$DRIGL_LIBS"
+    X11_INCLUDES="$X11_INCLUDES $DRIGL_CFLAGS $LIBUDEV_CFLAGS"
+    GL_LIB_DEPS="$DRIGL_LIBS $LIBUDEV_LIBS"
 
     # need DRM libs, $PTHREAD_LIBS, etc.
     GL_LIB_DEPS="$GL_LIB_DEPS $LIBDRM_LIBS -lm $PTHREAD_LIBS $DLOPEN_LIBS"
diff --git a/src/glx/Makefile.am b/src/glx/Makefile.am
index f01709b..854025d 100644
--- a/src/glx/Makefile.am
+++ b/src/glx/Makefile.am
@@ -92,6 +92,8 @@  libglx_la_SOURCES = \
 	  glxhash.c \
 	  dri2_glx.c \
 	  dri2.c \
+          dri3_glx.c \
+          dri3_common.c \
 	  applegl_glx.c
 
 GL_LIBS = \
diff --git a/src/glx/dri3_common.c b/src/glx/dri3_common.c
new file mode 100644
index 0000000..c758f96
--- /dev/null
+++ b/src/glx/dri3_common.c
@@ -0,0 +1,146 @@ 
+/*
+ * 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.
+ */
+
+/*
+ * This code is derived from src/egl/drivers/dri2/common.c which
+ * carries the following copyright:
+ * 
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kristian Høgsberg <krh@bitplanet.net>
+ *    Benjamin Franzke <benjaminfranzke@googlemail.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <GL/gl.h>
+#include "glapi.h"
+#include "glxclient.h"
+#include "xf86dri.h"
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include "xf86drm.h"
+#include "dri_common.h"
+#include "dri3_priv.h"
+
+#define DRIVER_MAP_DRI3_ONLY
+#include "pci_ids/pci_id_driver_map.h"
+
+#include <libudev.h>
+
+static struct udev_device *
+dri3_udev_device_new_from_fd(struct udev *udev, int fd)
+{
+   struct udev_device *device;
+   struct stat buf;
+
+   if (fstat(fd, &buf) < 0) {
+      ErrorMessageF("DRI3: failed to stat fd %d", fd);
+      return NULL;
+   }
+
+   device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev);
+   if (device == NULL) {
+      ErrorMessageF("DRI3: could not create udev device for fd %d", fd);
+      return NULL;
+   }
+
+   return device;
+}
+
+char *
+dri3_get_driver_for_fd(int fd)
+{
+   struct udev *udev;
+   struct udev_device *device, *parent;
+   const char *pci_id;
+   char *driver = NULL;
+   int vendor_id, chip_id, i, j;
+
+   udev = udev_new();
+   device = dri3_udev_device_new_from_fd(udev, fd);
+   if (device == NULL)
+      return NULL;
+
+   parent = udev_device_get_parent(device);
+   if (parent == NULL) {
+      ErrorMessageF("DRI3: could not get parent device");
+      goto out;
+   }
+
+   pci_id = udev_device_get_property_value(parent, "PCI_ID");
+   if (pci_id == NULL ||
+       sscanf(pci_id, "%x:%x", &vendor_id, &chip_id) != 2) {
+      ErrorMessageF("DRI3: malformed or no PCI ID");
+      goto out;
+   }
+
+   for (i = 0; driver_map[i].driver; i++) {
+      if (vendor_id != driver_map[i].vendor_id)
+         continue;
+      if (driver_map[i].num_chips_ids == -1) {
+         driver = strdup(driver_map[i].driver);
+         goto out;
+      }
+
+      for (j = 0; j < driver_map[i].num_chips_ids; j++)
+         if (driver_map[i].chip_ids[j] == chip_id) {
+            driver = strdup(driver_map[i].driver);
+            goto out;
+         }
+   }
+
+out:
+   udev_device_unref(device);
+   udev_unref(udev);
+
+   return driver;
+}
diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
new file mode 100644
index 0000000..4021baa
--- /dev/null
+++ b/src/glx/dri3_glx.c
@@ -0,0 +1,1722 @@ 
+/*
+ * 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.
+ */
+
+/*
+ * Portions of this code were adapted from dri2_glx.c which carries the
+ * following copyright:
+ *
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Soft-
+ * ware"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, provided that the above copyright
+ * notice(s) and this permission notice appear in all copies of the Soft-
+ * ware and that both the above copyright notice(s) and this permission
+ * notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+ * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
+ * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
+ * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
+ * QUENTIAL 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 PERFOR-
+ * MANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or
+ * other dealings in this Software without prior written authorization of
+ * the copyright holder.
+ *
+ * Authors:
+ *   Kristian Høgsberg (krh@redhat.com)
+ */
+
+#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/Xlib-xcb.h>
+#include <X11/xshmfence.h>
+#include <xcb/xcb.h>
+#include <xcb/dri3.h>
+#include <xcb/present.h>
+#include <GL/gl.h>
+#include "glapi.h"
+#include "glxclient.h"
+#include "xf86dri.h"
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "xf86drm.h"
+#include "dri_common.h"
+#include "dri3_priv.h"
+
+static const struct glx_context_vtable dri3_context_vtable;
+
+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 inline Bool
+dri3_fence_triggered(struct dri3_buffer *buffer) {
+   return xshmfence_query(buffer->shm_fence);
+}
+
+static void
+dri3_destroy_context(struct glx_context *context)
+{
+   struct dri3_context *pcp = (struct dri3_context *) context;
+   struct dri3_screen *psc = (struct dri3_screen *) context->psc;
+
+   driReleaseDrawables(&pcp->base);
+
+   free((char *) context->extensions);
+
+   (*psc->core->destroyContext) (pcp->driContext);
+
+   free(pcp);
+}
+
+static Bool
+dri3_bind_context(struct glx_context *context, struct glx_context *old,
+		  GLXDrawable draw, GLXDrawable read)
+{
+   struct dri3_context *pcp = (struct dri3_context *) context;
+   struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc;
+   struct dri3_drawable *pdraw, *pread;
+
+   pdraw = (struct dri3_drawable *) driFetchDrawable(context, draw);
+   pread = (struct dri3_drawable *) driFetchDrawable(context, read);
+
+   driReleaseDrawables(&pcp->base);
+
+   if (pdraw == NULL || pread == NULL)
+      return GLXBadDrawable;
+
+   if (!(*psc->core->bindContext) (pcp->driContext,
+				   pdraw->driDrawable, pread->driDrawable))
+      return GLXBadContext;
+
+   return Success;
+}
+
+static void
+dri3_unbind_context(struct glx_context *context, struct glx_context *new)
+{
+   struct dri3_context *pcp = (struct dri3_context *) context;
+   struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc;
+
+   (*psc->core->unbindContext) (pcp->driContext);
+}
+
+static struct glx_context *
+dri3_create_context(struct glx_screen *base,
+		    struct glx_config *config_base,
+		    struct glx_context *shareList, int renderType)
+{
+   struct dri3_context *pcp, *pcp_shared;
+   struct dri3_screen *psc = (struct dri3_screen *) base;
+   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
+   __DRIcontext *shared = NULL;
+
+   if (shareList) {
+      /* If the shareList context is not a DRI3 context, we cannot possibly
+       * create a DRI3 context that shares it.
+       */
+      if (shareList->vtable->destroy != dri3_destroy_context) {
+	 return NULL;
+      }
+
+      pcp_shared = (struct dri3_context *) shareList;
+      shared = pcp_shared->driContext;
+   }
+
+   pcp = calloc(1, sizeof *pcp);
+   if (pcp == NULL)
+      return NULL;
+
+   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
+      free(pcp);
+      return NULL;
+   }
+
+   pcp->driContext =
+      (*psc->image_driver->createNewContext) (psc->driScreen,
+                                              config->driConfig, shared, pcp);
+
+   if (pcp->driContext == NULL) {
+      free(pcp);
+      return NULL;
+   }
+
+   pcp->base.vtable = &dri3_context_vtable;
+
+   return &pcp->base;
+}
+
+static struct glx_context *
+dri3_create_context_attribs(struct glx_screen *base,
+			    struct glx_config *config_base,
+			    struct glx_context *shareList,
+			    unsigned num_attribs,
+			    const uint32_t *attribs,
+			    unsigned *error)
+{
+   struct dri3_context *pcp = NULL;
+   struct dri3_context *pcp_shared = NULL;
+   struct dri3_screen *psc = (struct dri3_screen *) base;
+   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
+   __DRIcontext *shared = NULL;
+
+   uint32_t minor_ver = 1;
+   uint32_t major_ver = 2;
+   uint32_t flags = 0;
+   unsigned api;
+   int reset = __DRI_CTX_RESET_NO_NOTIFICATION;
+   uint32_t ctx_attribs[2 * 5];
+   unsigned num_ctx_attribs = 0;
+   uint32_t render_type;
+
+   /* Remap the GLX tokens to DRI2 tokens.
+    */
+   if (!dri2_convert_glx_attribs(num_attribs, attribs,
+				 &major_ver, &minor_ver,
+                                 &render_type, &flags, &api,
+                                 &reset, error))
+      goto error_exit;
+
+   /* Check the renderType value */
+   if (!validate_renderType_against_config(config_base, render_type))
+       goto error_exit;
+
+   if (shareList) {
+      pcp_shared = (struct dri3_context *) shareList;
+      shared = pcp_shared->driContext;
+   }
+
+   pcp = calloc(1, sizeof *pcp);
+   if (pcp == NULL) {
+      *error = __DRI_CTX_ERROR_NO_MEMORY;
+      goto error_exit;
+   }
+
+   if (!glx_context_init(&pcp->base, &psc->base, &config->base))
+      goto error_exit;
+
+   ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MAJOR_VERSION;
+   ctx_attribs[num_ctx_attribs++] = major_ver;
+   ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MINOR_VERSION;
+   ctx_attribs[num_ctx_attribs++] = minor_ver;
+
+   /* Only send a value when the non-default value is requested.  By doing
+    * this we don't have to check the driver's DRI3 version before sending the
+    * default value.
+    */
+   if (reset != __DRI_CTX_RESET_NO_NOTIFICATION) {
+      ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_RESET_STRATEGY;
+      ctx_attribs[num_ctx_attribs++] = reset;
+   }
+
+   if (flags != 0) {
+      ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_FLAGS;
+
+      /* The current __DRI_CTX_FLAG_* values are identical to the
+       * GLX_CONTEXT_*_BIT values.
+       */
+      ctx_attribs[num_ctx_attribs++] = flags;
+   }
+
+   pcp->driContext =
+      (*psc->image_driver->createContextAttribs) (psc->driScreen,
+                                                  api,
+                                                  config->driConfig,
+                                                  shared,
+                                                  num_ctx_attribs / 2,
+                                                  ctx_attribs,
+                                                  error,
+                                                  pcp);
+
+   if (pcp->driContext == NULL)
+      goto error_exit;
+
+   pcp->base.vtable = &dri3_context_vtable;
+
+   return &pcp->base;
+
+error_exit:
+   free(pcp);
+
+   return NULL;
+}
+
+static void
+dri3_destroy_drawable(__GLXDRIdrawable *base)
+{
+   struct dri3_screen *psc = (struct dri3_screen *) base->psc;
+   struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
+
+   (*psc->core->destroyDrawable) (pdraw->driDrawable);
+
+   free(pdraw);
+}
+
+static __GLXDRIdrawable *
+dri3_create_drawable(struct glx_screen *base, XID xDrawable,
+                     GLXDrawable drawable, struct glx_config *config_base)
+{
+   struct dri3_drawable *pdraw;
+   struct dri3_screen *psc = (struct dri3_screen *) base;
+   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
+   GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
+
+   pdraw = calloc(1, sizeof(*pdraw));
+   if (!pdraw)
+      return NULL;
+
+   pdraw->base.destroyDrawable = dri3_destroy_drawable;
+   pdraw->base.xDrawable = xDrawable;
+   pdraw->base.drawable = drawable;
+   pdraw->base.psc = &psc->base;
+//   pdraw->bufferCount = 0;
+   pdraw->swap_interval = 1; /* default may be overridden below */
+   pdraw->have_back = 0;
+   pdraw->have_fake_front = 0;
+
+   if (psc->config)
+      psc->config->configQueryi(psc->driScreen,
+				"vblank_mode", &vblank_mode);
+
+   switch (vblank_mode) {
+   case DRI_CONF_VBLANK_NEVER:
+   case DRI_CONF_VBLANK_DEF_INTERVAL_0:
+      pdraw->swap_interval = 0;
+      break;
+   case DRI_CONF_VBLANK_DEF_INTERVAL_1:
+   case DRI_CONF_VBLANK_ALWAYS_SYNC:
+   default:
+      pdraw->swap_interval = 1;
+      break;
+   }
+
+   (void) __glXInitialize(psc->base.dpy);
+
+   /* Create a new drawable */
+   pdraw->driDrawable =
+      (*psc->image_driver->createNewDrawable) (psc->driScreen,
+                                               config->driConfig, pdraw);
+
+   if (!pdraw->driDrawable) {
+      free(pdraw);
+      return NULL;
+   }
+
+   /*
+    * Make sure server has the same swap interval we do for the new
+    * drawable.
+    */
+   if (psc->vtable.setSwapInterval)
+      psc->vtable.setSwapInterval(&pdraw->base, pdraw->swap_interval);
+
+   return &pdraw->base;
+}
+
+static void
+present_handle_special_event(struct dri3_drawable *priv, xcb_present_generic_event_t *ge)
+{
+   switch (ge->evtype) {
+   case XCB_PRESENT_CONFIGURE_NOTIFY: {
+      xcb_present_configure_notify_event_t *ce = (void *) ge;
+
+      priv->width = ce->width;
+      priv->height = ce->height;
+      break;
+   }
+   case XCB_PRESENT_COMPLETE_NOTIFY: {
+      xcb_present_complete_notify_event_t *ce = (void *) ge;
+
+      if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP)
+         priv->present_event_serial = ce->serial;
+      else
+         priv->present_msc_event_serial = ce->serial;
+      priv->ust = ce->ust;
+      priv->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 (priv->buffers) / sizeof (priv->buffers[0]); b++) {
+         struct dri3_buffer        *buf = priv->buffers[b];
+
+         if (buf && buf->pixmap == ie->pixmap) {
+            buf->busy = 0;
+            break;
+         }
+      }
+      break;
+   }
+   }
+   free(ge);
+}
+
+static int
+dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
+                  int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc)
+{
+   xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
+   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
+   xcb_generic_event_t *ev;
+   xcb_present_generic_event_t *ge;
+
+   /* Ask for the an event for the target MSC */
+   ++priv->present_msc_request_serial;
+   xcb_present_notify_msc(c,
+                          priv->base.xDrawable,
+                          priv->present_msc_request_serial,
+                          target_msc,
+                          divisor,
+                          remainder);
+
+   xcb_flush(c);
+
+   /* Wait for the event */
+   if (priv->special_event) {
+      while (priv->present_msc_request_serial != priv->present_msc_event_serial) {
+         ev = xcb_wait_for_special_event(c, priv->special_event);
+         if (!ev)
+            break;
+         ge = (void *) ev;
+         present_handle_special_event(priv, ge);
+      }
+   }
+
+   *ust = priv->ust;
+   *msc = priv->msc;
+
+   *sbc = priv->sbc;
+
+   return 1;
+}
+
+static int
+dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw,
+                      int64_t *ust, int64_t *msc, int64_t *sbc)
+{
+   return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc,sbc);
+}
+
+static int
+dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust,
+                  int64_t *msc, int64_t *sbc)
+{
+   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
+
+   while (priv->sbc < target_sbc) {
+      sleep(1);
+   }
+   return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc, sbc);
+}
+
+static __DRIcontext *
+dri3_get_current_context(void)
+{
+   struct glx_context *gc = __glXGetCurrentContext();
+   struct dri3_context *dri3Ctx = (struct dri3_context *)gc;
+
+   return dri3Ctx ? dri3Ctx->driContext : NULL;
+}
+
+/**
+ * dri3Throttle - Request driver throttling
+ *
+ * This function uses the DRI2 throttle extension to give the
+ * driver the opportunity to throttle on flush front, copysubbuffer
+ * and swapbuffers.
+ */
+static void
+dri3_throttle(struct dri3_screen *psc,
+              struct dri3_drawable *draw,
+              enum __DRI2throttleReason reason)
+{
+   if (psc->throttle) {
+      __DRIcontext *ctx = dri3_get_current_context();
+
+      psc->throttle->throttle(ctx, draw->driDrawable, reason);
+   }
+}
+
+/**
+ * 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 dri3_screen *psc,
+           __DRIcontext *ctx,
+           struct dri3_drawable *draw,
+           unsigned flags,
+           enum __DRI2throttleReason throttle_reason)
+{
+   if (ctx && psc->f && psc->f->base.version >= 4) {
+      psc->f->flush_with_flags(ctx, draw->driDrawable, flags, throttle_reason);
+   } else {
+      if (flags & __DRI2_FLUSH_CONTEXT)
+         glFlush();
+
+      if (psc->f)
+         psc->f->flush(draw->driDrawable);
+
+      dri3_throttle(psc, draw, throttle_reason);
+   }
+}
+
+static xcb_gcontext_t
+dri3_drawable_gc(struct dri3_drawable *priv)
+{
+   if (!priv->gc) {
+      uint32_t v;
+      xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
+
+      v = 0;
+      xcb_create_gc(c,
+                    (priv->gc = xcb_generate_id(c)),
+                    priv->base.xDrawable,
+                    XCB_GC_GRAPHICS_EXPOSURES,
+                    &v);
+   }
+   return priv->gc;
+}
+
+static struct dri3_buffer *
+dri3_back_buffer(struct dri3_drawable *priv)
+{
+   return priv->buffers[DRI3_BACK_ID(priv->cur_back)];
+}
+
+static struct dri3_buffer *
+dri3_fake_front_buffer(struct dri3_drawable *priv)
+{
+   return priv->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);
+}
+
+static void
+_dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
+                       int width, int height,
+                       enum __DRI2throttleReason reason, Bool flush)
+{
+   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
+   struct dri3_screen *psc = (struct dri3_screen *) pdraw->psc;
+   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
+   __DRIcontext *ctx = dri3_get_current_context();
+   struct dri3_buffer *back = dri3_back_buffer(priv);
+
+   unsigned flags;
+
+   /* Check we have the right attachments */
+   if (!priv->have_back || priv->is_pixmap)
+      return;
+
+   flags = __DRI2_FLUSH_DRAWABLE;
+   if (flush)
+      flags |= __DRI2_FLUSH_CONTEXT;
+   dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
+
+   y = priv->height - y - height;
+
+   dri3_fence_reset(c, back);
+   dri3_copy_area(c,
+                  dri3_back_buffer(priv)->pixmap,
+                  priv->base.xDrawable,
+                  dri3_drawable_gc(priv),
+                  x, y, x, y, width, height);
+   dri3_fence_trigger(c, back);
+   /* Refresh the fake front (if present) after we just damaged the real
+    * front.
+    */
+   if (priv->have_fake_front) {
+      dri3_fence_reset(c, dri3_fake_front_buffer(priv));
+      dri3_copy_area(c,
+                     dri3_back_buffer(priv)->pixmap,
+                     dri3_fake_front_buffer(priv)->pixmap,
+                     dri3_drawable_gc(priv),
+                     x, y, x, y, width, height);
+      dri3_fence_trigger(c, dri3_fake_front_buffer(priv));
+      dri3_fence_await(c, dri3_fake_front_buffer(priv));
+   }
+   dri3_fence_await(c, back);
+}
+
+static void
+dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
+		  int width, int height, Bool flush)
+{
+   _dri3_copy_sub_buffer(pdraw, x, y, width, height,
+                         __DRI2_THROTTLE_COPYSUBBUFFER, flush);
+}
+
+
+static void
+dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src)
+{
+   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
+   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
+
+   if (psc->f)
+      (*psc->f->flush) (priv->driDrawable);
+
+   dri3_copy_area(c,
+                  src, dest,
+                  dri3_drawable_gc(priv),
+                  0, 0, 0, 0, priv->width, priv->height);
+}
+
+static void
+dri3_wait_x(struct glx_context *gc)
+{
+   struct dri3_drawable *priv = (struct dri3_drawable *)
+      GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
+
+   if (priv == NULL || !priv->have_fake_front)
+      return;
+
+   dri3_copy_drawable(priv, dri3_fake_front_buffer(priv)->pixmap, priv->base.xDrawable);
+}
+
+static void
+dri3_wait_gl(struct glx_context *gc)
+{
+   struct dri3_drawable *priv = (struct dri3_drawable *)
+      GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
+
+   if (priv == NULL || !priv->have_fake_front)
+      return;
+
+   dri3_copy_drawable(priv, priv->base.xDrawable, dri3_fake_front_buffer(priv)->pixmap);
+}
+
+/**
+ * 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)
+{
+   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_throttle(psc, pdraw, __DRI2_THROTTLE_FLUSHFRONT);
+
+   dri3_wait_gl(gc);
+}
+
+static struct dri3_buffer *
+dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, unsigned int format, int width, int height, int depth)
+{
+   struct dri3_screen *psc = (struct dri3_screen *) glx_screen;
+   Display *dpy = glx_screen->dpy;
+   struct dri3_buffer *buffer;
+   xcb_connection_t *c = XGetXCBConnection(dpy);
+   xcb_pixmap_t pixmap;
+   xcb_sync_fence_t sync_fence;
+   int32_t *shm_fence;
+   int buffer_fd, fence_fd;
+   int stride;
+
+   fence_fd = xshmfence_alloc_shm();
+   if (fence_fd < 0)
+      return NULL;
+   shm_fence = xshmfence_map_shm(fence_fd);
+   if (shm_fence == NULL)
+      goto no_shm_fence;
+
+   buffer = calloc(1, sizeof (struct dri3_buffer));
+   if (!buffer)
+      goto no_buffer;
+
+   buffer->image = (*psc->image->createImage) (psc->driScreen,
+                                               width, height,
+                                               format,
+                                               __DRI_IMAGE_USE_SHARE|__DRI_IMAGE_USE_SCANOUT,
+                                               buffer);
+
+   
+   if (!buffer->image)
+      goto no_image;
+
+   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_STRIDE, &stride))
+      goto no_buffer_attrib;
+
+   buffer->pitch = stride;
+
+   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_FD, &buffer_fd))
+      goto no_buffer_attrib;
+
+   xcb_dri3_pixmap_from_buffer(c,
+                               (pixmap = xcb_generate_id(c)),
+                               draw,
+                               buffer->size,
+                               width, height, buffer->pitch,
+                               depth, buffer->cpp * 8,
+                               buffer_fd);
+
+   xcb_dri3_fence_from_fd(c,
+                          pixmap,
+                          (sync_fence = xcb_generate_id(c)),
+                          false,
+                          fence_fd);
+
+   buffer->pixmap = pixmap;
+   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:
+   (*psc->image->destroyImage)(buffer->image);
+no_image:
+   free(buffer);
+no_buffer:
+   xshmfence_unmap_shm(shm_fence);
+no_shm_fence:
+   close(fence_fd);
+   return NULL;
+}
+
+static void
+dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer)
+{
+   struct dri3_screen   *psc = (struct dri3_screen *) pdraw->base.psc;
+   xcb_connection_t     *c = XGetXCBConnection(pdraw->base.psc->dpy);
+
+   xcb_free_pixmap(c, buffer->pixmap);
+   xcb_sync_destroy_fence(c, buffer->sync_fence);
+   xshmfence_unmap_shm(buffer->shm_fence);
+   (*psc->image->destroyImage)(buffer->image);
+   free(buffer);
+}
+
+
+
+static void
+present_flush_events(struct dri3_drawable *priv)
+{
+   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
+
+   /* Check to see if any configuration changes have occurred
+    * since we were last invoked
+    */
+   if (priv->special_event) {
+      xcb_generic_event_t    *ev;
+
+      while ((ev = xcb_check_for_special_event(c, priv->special_event)) != NULL) {
+         xcb_present_generic_event_t *ge = (void *) ev;
+         present_handle_special_event(priv, ge);
+      }
+   }
+}
+
+static int
+dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
+{
+   struct dri3_drawable *priv = loaderPrivate;
+   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
+
+   /* First time through, go get the current drawable geometry
+    */
+   if (priv->width == 0 || priv->height == 0 || priv->depth == 0) {
+      xcb_get_geometry_cookie_t                 geom_cookie;
+      xcb_get_geometry_reply_t                  *geom_reply;
+      xcb_void_cookie_t                         cookie;
+      xcb_generic_error_t                       *error;
+
+      cookie = xcb_present_select_input_checked(c,
+                                                (priv->eid = xcb_generate_id(c)),
+                                                priv->base.xDrawable,
+                                                XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY|
+                                                XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
+                                                XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
+         
+      if (!priv->present_extension) {
+         priv->present_extension = xcb_get_extension_data(c, &xcb_present_id);
+         if (!priv->present_extension)
+            return false;
+      }
+
+      priv->special_event = xcb_register_for_special_event(c,
+                                                           priv->present_extension->major_opcode,
+                                                           priv->eid,
+                                                           priv->stamp);
+
+      geom_cookie = xcb_get_geometry(c, priv->base.xDrawable);
+
+      geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL);
+
+      if (!geom_reply)
+         return false;
+
+      priv->width = geom_reply->width;
+      priv->height = geom_reply->height;
+      priv->depth = geom_reply->depth;
+      priv->is_pixmap = false;
+
+      free(geom_reply);
+
+      error = xcb_request_check(c, cookie);
+
+      if (error) {
+         if (error->error_code != BadWindow) {
+            free(error);
+            return false;
+         }
+         priv->is_pixmap = true;
+         xcb_unregister_for_special_event(c, priv->special_event);
+         priv->special_event = NULL;
+      }
+   }
+   present_flush_events(priv);
+   return true;
+}
+   
+static int
+image_format_to_fourcc(int format)
+{
+
+   /* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */
+   switch (format) {
+   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;
+//   case __DRI_IMAGE_FORMAT_R8: return __DRI_IMAGE_FOURCC_R8;
+//   case __DRI_IMAGE_FORMAT_GR88: return __DRI_IMAGE_FOURCC_GR88;
+//   case __DRI_IMAGE_FORMAT_NONE: return __DRI_IMAGE_FOURCC_NONE;
+//   case __DRI_IMAGE_FORMAT_XRGB2101010: return __DRI_IMAGE_FOURCC_XRGB2101010;
+//   case __DRI_IMAGE_FORMAT_ARGB2101010: return __DRI_IMAGE_FOURCC_ARGB2101010;
+   }
+   return 0;
+}
+
+static struct dri3_buffer *
+dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
+                       unsigned int format,
+                       enum dri3_buffer_type buffer_type,
+                       void *loaderPrivate)
+{
+   struct dri3_drawable                 *pdraw = loaderPrivate;
+   int                                  buf_id = buffer_type == dri3_pixmap_buf_id(buffer_type);
+   struct dri3_buffer                   *buffer = pdraw->buffers[buf_id];
+   Pixmap                               pixmap;
+   xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
+   xcb_dri3_buffer_from_pixmap_reply_t  *bp_reply;
+   int                                  *fds;
+   int                                  buffer_fd;
+   Display                              *dpy;
+   struct dri3_screen                   *psc;
+   xcb_connection_t                     *c;
+   xcb_sync_fence_t                     sync_fence;
+   int32_t                              *shm_fence;
+   int                                  fence_fd;
+   __DRIimage                           *image_planar;
+   int                                  stride, offset;
+
+   if (buffer)
+      return buffer;
+
+   pixmap = pdraw->base.xDrawable;
+   psc = (struct dri3_screen *) pdraw->base.psc;
+   dpy = psc->base.dpy;
+   c = XGetXCBConnection(dpy);
+
+   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(c,
+                          pixmap,
+                          (sync_fence = xcb_generate_id(c)),
+                          false,
+                          fence_fd);
+
+   bp_cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
+   bp_reply = xcb_dri3_buffer_from_pixmap_reply(c, bp_cookie, NULL);
+   if (!bp_reply)
+      goto no_image;
+   fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply);
+   buffer_fd = fds[0];
+
+   stride = bp_reply->stride;
+   offset = 0;
+
+   image_planar = (*psc->image->createImageFromFds) (psc->driScreen,
+                                                     bp_reply->width,
+                                                     bp_reply->height,
+                                                     image_format_to_fourcc(format),
+                                                     fds, 1,
+                                                     &stride, &offset, buffer);
+   close(buffer_fd);
+   if (!image_planar)
+      goto no_image;
+
+   buffer->image = (*psc->image->fromPlanar)(image_planar, 0, buffer);
+
+   (*psc->image->destroyImage)(image_planar);
+
+   if (!buffer->image)
+      goto no_image;
+
+   buffer->pixmap = pixmap;
+   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;
+
+   pdraw->buffers[buf_id] = buffer;
+   return buffer;
+
+no_image:
+   xcb_sync_destroy_fence(c, sync_fence);
+   xshmfence_unmap_shm(shm_fence);
+no_fence:
+   free(buffer);
+no_buffer:
+   return NULL;
+}
+
+static int
+dri3_find_back(xcb_connection_t *c, struct dri3_drawable *priv)
+{
+   int  b;
+   xcb_generic_event_t *ev;
+   xcb_present_generic_event_t *ge;
+
+   for (;;) {
+
+      for (b = 0; b < DRI3_MAX_BACK; b++) {
+         int                    id = DRI3_BACK_ID(b);
+         struct dri3_buffer        *buffer = priv->buffers[id];
+
+         if (!buffer)
+            return b;
+         if (!buffer->busy)
+            return b;
+      }
+      ev = xcb_wait_for_special_event(c, priv->special_event);
+      if (!ev)
+         return -1;
+      ge = (void *) ev;
+      present_handle_special_event(priv, ge);
+   } 
+}
+
+static struct dri3_buffer *
+dri3_get_buffer(__DRIdrawable *driDrawable,
+                unsigned int format,
+                enum dri3_buffer_type buffer_type,
+                void *loaderPrivate)
+{
+   struct dri3_drawable *priv = loaderPrivate;
+   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
+   struct dri3_buffer      *buffer;
+   int                  buf_id;
+
+   if (buffer_type == dri3_buffer_back) {
+      int back = dri3_find_back(c, priv);
+
+      if (back < 0)
+         return NULL;
+
+      priv->cur_back = back;
+      buf_id = DRI3_BACK_ID(priv->cur_back);
+   } else {
+      buf_id = DRI3_FRONT_ID;
+   }
+
+   buffer = priv->buffers[buf_id];
+   if (!buffer || buffer->width != priv->width || buffer->height != priv->height) {
+      struct dri3_buffer   *new_buffer;
+
+      /* Allocate the new buffers
+       */
+      new_buffer = dri3_alloc_render_buffer(priv->base.psc,
+                                            priv->base.xDrawable,
+                                            format, priv->width, priv->height, priv->depth);
+      if (!new_buffer)
+         return NULL;
+      switch (buffer_type) {
+      case dri3_buffer_back:
+         if (buffer) {
+            dri3_fence_reset(c, new_buffer);
+            dri3_fence_await(c, buffer);
+            dri3_copy_area(c,
+                           buffer->pixmap,
+                           new_buffer->pixmap,
+                           dri3_drawable_gc(priv),
+                           0, 0, 0, 0, priv->width, priv->height);
+            dri3_fence_trigger(c, new_buffer);
+            dri3_free_render_buffer(priv, buffer);
+         }
+         break;
+      case dri3_buffer_front:
+         dri3_fence_reset(c, new_buffer);
+         dri3_copy_area(c,
+                        priv->base.xDrawable,
+                        new_buffer->pixmap,
+                        dri3_drawable_gc(priv),
+                        0, 0, 0, 0, priv->width, priv->height);
+         dri3_fence_trigger(c, new_buffer);
+         break;
+      }
+      buffer = new_buffer;
+      buffer->buffer_type = buffer_type;
+      priv->buffers[buf_id] = buffer;
+   } 
+   dri3_fence_await(c, buffer);
+
+   /* Return the requested buffer */
+   return buffer;
+}
+
+static void
+dri3_free_buffers(__DRIdrawable *driDrawable,
+                 enum dri3_buffer_type buffer_type,
+                 void *loaderPrivate)
+{
+   struct dri3_drawable *priv = loaderPrivate;
+   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 = priv->buffers[buf_id];
+      if (buffer) {
+         dri3_free_render_buffer(priv, buffer);
+         priv->buffers[buf_id] = NULL;
+      }
+   }
+}
+
+static int
+dri3_get_buffers(__DRIdrawable *driDrawable,
+                 int *width, int *height,
+                 unsigned int format,
+                 uint32_t *stamp,
+                 void *loaderPrivate,
+                 uint32_t buffer_mask,
+                 struct __DRIimageList *buffers)
+{
+   struct dri3_drawable *priv = loaderPrivate;
+   struct dri3_buffer   *front, *back;
+
+   buffers->front = NULL;
+   buffers->back = NULL;
+
+   front = NULL;
+   back = NULL;
+
+   if (!dri3_update_drawable(driDrawable, loaderPrivate))
+      return false;
+
+   if (priv->is_pixmap)
+      buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
+
+   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
+      if (priv->is_pixmap)
+         front = dri3_get_pixmap_buffer(driDrawable,
+                                        format,
+                                        dri3_buffer_front,
+                                        loaderPrivate);
+      else
+         front = dri3_get_buffer(driDrawable,
+                                 format,
+                                 dri3_buffer_front,
+                                 loaderPrivate);
+
+      if (!front)
+         return false;
+      priv->have_fake_front = !priv->is_pixmap;
+   } else {
+      dri3_free_buffers(driDrawable, dri3_buffer_front, loaderPrivate);
+      priv->have_fake_front = 0;
+   }
+
+   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
+      back = dri3_get_buffer(driDrawable,
+                             format,
+                             dri3_buffer_back,
+                             loaderPrivate);
+      if (!back)
+         return false;
+      priv->have_back = 1;
+   } else {
+      dri3_free_buffers(driDrawable, dri3_buffer_back, loaderPrivate);
+      priv->have_back = 0;
+   }
+
+   if (front)
+      buffers->front = front->image;
+   
+   if (back)
+      buffers->back = back->image;
+
+   priv->stamp = stamp;
+
+   /* Report back current geometry */
+   *width = priv->width;
+   *height = priv->height;
+   return true;
+}
+
+static int64_t
+dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
+                  int64_t remainder, Bool flush)
+{
+   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
+   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
+   Display *dpy = priv->base.psc->dpy;
+   xcb_connection_t *c = XGetXCBConnection(dpy);
+   int buf_id = DRI3_BACK_ID(priv->cur_back);
+   int64_t ret = 0;
+
+   __DRIcontext *ctx = dri3_get_current_context();
+   unsigned flags = __DRI2_FLUSH_DRAWABLE;
+   if (flush)
+      flags |= __DRI2_FLUSH_CONTEXT;
+   dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
+    
+   present_flush_events(priv);
+
+   if (priv->buffers[buf_id] && !priv->is_pixmap) {
+      dri3_fence_reset(c, priv->buffers[buf_id]);
+
+      /* 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
+       */
+      ++priv->present_request_serial;
+      if (target_msc == 0)
+         target_msc = priv->msc + priv->swap_interval * (priv->present_request_serial - priv->present_event_serial);
+
+      priv->buffers[buf_id]->busy = 1;
+      xcb_present_pixmap(c,
+                         priv->base.xDrawable,
+                         priv->buffers[buf_id]->pixmap,
+                         priv->present_request_serial,
+                         0,                                    /* valid */
+                         0,                                    /* update */
+                         0,                                    /* x_off */
+                         0,                                    /* y_off */
+                         None,                                 /* target_crtc */
+                         None,
+                         priv->buffers[buf_id]->sync_fence,
+                         XCB_PRESENT_OPTION_NONE,
+                         target_msc,
+                         divisor,
+                         remainder, 0, NULL);
+      ret = ++priv->sbc;
+      if (priv->have_fake_front) {
+         dri3_fence_reset(c, priv->buffers[DRI3_FRONT_ID]);
+         dri3_copy_area(c,
+                        priv->buffers[buf_id]->pixmap,
+                        priv->buffers[DRI3_FRONT_ID]->pixmap,
+                        dri3_drawable_gc(priv),
+                        0, 0, 0, 0, priv->width, priv->height);
+         dri3_fence_trigger(c, priv->buffers[DRI3_FRONT_ID]);
+      }
+      xcb_flush(c);
+      if (priv->stamp)
+         ++(*priv->stamp);
+   }
+
+   return ret;
+}
+
+static int
+dri3_query_version(Display *dpy, int *major, int *minor)
+{
+   xcb_dri3_query_version_cookie_t      cookie;
+   xcb_dri3_query_version_reply_t       *reply;       
+   xcb_connection_t                     *c = XGetXCBConnection(dpy);
+   xcb_generic_error_t                  *error;
+
+   cookie = xcb_dri3_query_version(c,
+                                   XCB_DRI3_MAJOR_VERSION,
+                                   XCB_DRI3_MINOR_VERSION);
+   reply = xcb_dri3_query_version_reply(c, cookie, &error);
+   if (!reply) {
+      if (error) {
+         free(error);
+      }
+      return 0;
+   }
+   *major = reply->major_version;
+   *minor = reply->minor_version;
+   free(reply);
+   return 1;
+}
+
+static int
+present_query_version(Display *dpy, int *major, int *minor)
+{
+   xcb_present_query_version_cookie_t   cookie;
+   xcb_present_query_version_reply_t    *reply;       
+   xcb_connection_t                     *c = XGetXCBConnection(dpy);
+   xcb_generic_error_t                  *error;
+
+   cookie = xcb_present_query_version(c,
+                                   XCB_PRESENT_MAJOR_VERSION,
+                                   XCB_PRESENT_MINOR_VERSION);
+   reply = xcb_present_query_version_reply(c, cookie, &error);
+   if (!reply) {
+      if (error) {
+         free(error);
+      }
+      return 0;
+   }
+   *major = reply->major_version;
+   *minor = reply->minor_version;
+   free(reply);
+   return 1;
+}
+
+static int
+dri3_open(Display *dpy,
+          Window root,
+          CARD32 provider)
+{
+   xcb_dri3_open_cookie_t       cookie;
+   xcb_dri3_open_reply_t        *reply;       
+   xcb_connection_t             *c = XGetXCBConnection(dpy);
+   xcb_generic_error_t          *error;
+   int                          fd;
+
+   cookie = xcb_dri3_open(c,
+                          root,
+                          provider);
+
+   reply = xcb_dri3_open_reply(c, cookie, &error);
+   if (!reply)
+      return -1;
+
+   if (reply->nfd != 1) {
+      free(reply);
+      return -1;
+   }
+
+   fd = xcb_dri3_open_reply_fds(c, reply)[0];
+   fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+   return fd;
+}
+
+
+static void
+dri3_destroy_screen(struct glx_screen *base)
+{
+   struct dri3_screen *psc = (struct dri3_screen *) base;
+
+   /* Free the direct rendering per screen data */
+   (*psc->core->destroyScreen) (psc->driScreen);
+   driDestroyConfigs(psc->driver_configs);
+   close(psc->fd);
+   free(psc);
+}
+
+static int
+dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval)
+{
+   struct dri3_drawable *priv =  (struct dri3_drawable *) pdraw;
+   GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1;
+   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
+
+   if (psc->config)
+      psc->config->configQueryi(psc->driScreen,
+				"vblank_mode", &vblank_mode);
+
+   switch (vblank_mode) {
+   case DRI_CONF_VBLANK_NEVER:
+      if (interval != 0)
+         return GLX_BAD_VALUE;
+      break;
+   case DRI_CONF_VBLANK_ALWAYS_SYNC:
+      if (interval <= 0)
+	 return GLX_BAD_VALUE;
+      break;
+   default:
+      break;
+   }
+
+   priv->swap_interval = interval;
+
+   return 0;
+}
+
+static int
+dri3_get_swap_interval(__GLXDRIdrawable *pdraw)
+{
+   struct dri3_drawable *priv =  (struct dri3_drawable *) pdraw;
+
+  return priv->swap_interval;
+}
+
+static const __DRIimageLoaderExtension imageLoaderExtension = {
+   {__DRI_IMAGE_LOADER, __DRI_IMAGE_LOADER_VERSION},
+   .getBuffers = dri3_get_buffers,
+   .flushFrontBuffer = dri3_flush_front_buffer,
+};
+
+static void
+dri3_bind_tex_image(Display * dpy,
+		    GLXDrawable drawable,
+		    int buffer, const int *attrib_list)
+{
+   struct glx_context *gc = __glXGetCurrentContext();
+   struct dri3_context *pcp = (struct dri3_context *) gc;
+   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
+   struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
+   struct dri3_screen *psc;
+
+   if (pdraw != NULL) {
+      psc = (struct dri3_screen *) base->psc;
+
+      if (psc->f &&
+           psc->f->base.version >= 3 && psc->f->invalidate)
+	 psc->f->invalidate(pdraw->driDrawable);
+
+      XSync(dpy, false);
+      if (psc->texBuffer->base.version >= 2 &&
+	  psc->texBuffer->setTexBuffer2 != NULL) {
+	 (*psc->texBuffer->setTexBuffer2) (pcp->driContext,
+					   pdraw->base.textureTarget,
+					   pdraw->base.textureFormat,
+					   pdraw->driDrawable);
+      }
+      else {
+	 (*psc->texBuffer->setTexBuffer) (pcp->driContext,
+					  pdraw->base.textureTarget,
+					  pdraw->driDrawable);
+      }
+   }
+}
+
+static void
+dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
+{
+#if __DRI_TEX_BUFFER_VERSION >= 3
+   struct glx_context *gc = __glXGetCurrentContext();
+   struct dri3_context *pcp = (struct dri3_context *) gc;
+   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
+   struct glx_display *dpyPriv = __glXInitialize(dpy);
+   struct dri3_drawable *pdraw = (struct dri3_drawable *) base;
+   struct dri3_display *pdp =
+      (struct dri3_display *) dpyPriv->dri3Display;
+   struct dri3_screen *psc;
+
+   if (pdraw != NULL) {
+      psc = (struct dri3_screen *) base->psc;
+
+      if (psc->texBuffer->base.version >= 3 &&
+          psc->texBuffer->releaseTexBuffer != NULL) {
+         (*psc->texBuffer->releaseTexBuffer) (pcp->driContext,
+                                           pdraw->base.textureTarget,
+                                           pdraw->driDrawable);
+      }
+   }
+#endif
+}
+
+static const struct glx_context_vtable dri3_context_vtable = {
+   dri3_destroy_context,
+   dri3_bind_context,
+   dri3_unbind_context,
+   dri3_wait_gl,
+   dri3_wait_x,
+   DRI_glXUseXFont,
+   dri3_bind_tex_image,
+   dri3_release_tex_image,
+   NULL, /* get_proc_address */
+};
+
+static void
+dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
+                     const char *driverName)
+{
+//   const struct dri3_display *const pdp = (struct dri3_display *) priv->dri3Display;
+   const __DRIextension **extensions;
+   unsigned mask;
+   int i;
+
+   extensions = psc->core->getExtensions(psc->driScreen);
+
+   __glXEnableDirectExtension(&psc->base, "GLX_SGI_video_sync");
+   __glXEnableDirectExtension(&psc->base, "GLX_SGI_swap_control");
+   __glXEnableDirectExtension(&psc->base, "GLX_MESA_swap_control");
+   __glXEnableDirectExtension(&psc->base, "GLX_SGI_make_current_read");
+
+   /*
+    * GLX_INTEL_swap_event is broken on the server side, where it's
+    * currently unconditionally enabled. This completely breaks
+    * systems running on drivers which don't support that extension.
+    * There's no way to test for its presence on this side, so instead
+    * of disabling it unconditionally, just disable it for drivers
+    * which are known to not support it, or for DDX drivers supporting
+    * only an older (pre-ScheduleSwap) version of DRI2.
+    *
+    * This is a hack which is required until:
+    * http://lists.x.org/archives/xorg-devel/2013-February/035449.html
+    * is merged and updated xserver makes it's way into distros:
+    */
+//   if (pdp->swapAvailable && strcmp(driverName, "vmwgfx") != 0) {
+//      __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event");
+//   }
+
+   mask = psc->image_driver->getAPIMask(psc->driScreen);
+
+   __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context");
+   __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context_profile");
+
+   if ((mask & (1 << __DRI_API_GLES2)) != 0)
+      __glXEnableDirectExtension(&psc->base,
+                                 "GLX_EXT_create_context_es2_profile");
+
+   for (i = 0; extensions[i]; i++) {
+      if ((strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
+	 psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
+	 __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
+      }
+
+      if ((strcmp(extensions[i]->name, __DRI2_FLUSH) == 0)) {
+	 psc->f = (__DRI2flushExtension *) extensions[i];
+	 /* internal driver extension, no GL extension exposed */
+      }
+
+      if ((strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0))
+	 psc->config = (__DRI2configQueryExtension *) extensions[i];
+
+      if (((strcmp(extensions[i]->name, __DRI2_THROTTLE) == 0)))
+	 psc->throttle = (__DRI2throttleExtension *) extensions[i];
+
+      if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0)
+         __glXEnableDirectExtension(&psc->base,
+                                    "GLX_ARB_create_context_robustness");
+   }
+}
+
+static const struct glx_screen_vtable dri3_screen_vtable = {
+   dri3_create_context,
+   dri3_create_context_attribs
+};
+
+static struct glx_screen *
+dri3_create_screen(int screen, struct glx_display * priv)
+{
+   const __DRIconfig **driver_configs;
+   const __DRIextension **extensions;
+   const struct dri3_display *const pdp = (struct dri3_display *)
+      priv->dri3Display;
+   struct dri3_screen *psc;
+   __GLXDRIscreen *psp;
+   struct glx_config *configs = NULL, *visuals = NULL;
+   char *driverName, *deviceName, *tmp;
+   int i;
+
+   psc = calloc(1, sizeof *psc);
+   if (psc == NULL)
+      return NULL;
+
+   psc->fd = -1;
+
+   if (!glx_screen_init(&psc->base, screen, priv)) {
+      free(psc);
+      return NULL;
+   }
+
+   psc->fd = dri3_open(priv->dpy, RootWindow(priv->dpy, screen), None);
+   if (psc->fd < 0) {
+      glx_screen_cleanup(&psc->base);
+      free(psc);
+      InfoMessageF("screen %d does not appear to be DRI3 capable\n", screen);
+      return NULL;
+   }
+   deviceName = NULL;
+
+   driverName = dri3_get_driver_for_fd(psc->fd);
+   if (!driverName) {
+      ErrorMessageF("No driver found\n");
+      goto handle_error;
+   }
+
+   psc->driver = driOpenDriver(driverName);
+   if (psc->driver == NULL) {
+      ErrorMessageF("driver pointer missing\n");
+      goto handle_error;
+   }
+
+   extensions = driGetDriverExtensions(psc->driver, driverName);
+   if (extensions == NULL)
+      goto handle_error;
+
+   for (i = 0; extensions[i]; i++) {
+      if (strcmp(extensions[i]->name, __DRI_CORE) == 0)
+	 psc->core = (__DRIcoreExtension *) extensions[i];
+      if (strcmp(extensions[i]->name, __DRI_IMAGE_DRIVER) == 0)
+	 psc->image_driver = (__DRIimageDriverExtension *) extensions[i];
+   }
+
+
+   if (psc->core == NULL) {
+      ErrorMessageF("core dri driver extension not found\n");
+      goto handle_error;
+   }
+
+   if (psc->image_driver == NULL) {
+      ErrorMessageF("image driver extension not found\n");
+      goto handle_error;
+   }
+
+   psc->driScreen =
+      psc->image_driver->createNewScreen2(screen, psc->fd,
+                                          (const __DRIextension **)
+                                          &pdp->loader_extensions[0],
+                                          extensions,
+                                          &driver_configs, psc);
+
+   if (psc->driScreen == NULL) {
+      ErrorMessageF("failed to create dri screen\n");
+      goto handle_error;
+   }
+
+   extensions = (*psc->core->getExtensions)(psc->driScreen);
+
+   for (i = 0; extensions[i]; i++) {
+      if (strcmp(extensions[i]->name, __DRI_IMAGE) == 0)
+         psc->image = (__DRIimageExtension *) extensions[i];
+   }
+
+   if (psc->image == NULL) {
+      ErrorMessageF("image extension not found\n");
+      goto handle_error;
+   }
+
+   dri3_bind_extensions(psc, priv, driverName);
+
+   configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs);
+   visuals = driConvertConfigs(psc->core, psc->base.visuals, driver_configs);
+
+   if (!configs || !visuals)
+       goto handle_error;
+
+   glx_config_destroy_list(psc->base.configs);
+   psc->base.configs = configs;
+   glx_config_destroy_list(psc->base.visuals);
+   psc->base.visuals = visuals;
+
+   psc->driver_configs = driver_configs;
+
+   psc->base.vtable = &dri3_screen_vtable;
+   psp = &psc->vtable;
+   psc->base.driScreen = psp;
+   psp->destroyScreen = dri3_destroy_screen;
+   psp->createDrawable = dri3_create_drawable;
+   psp->swapBuffers = dri3_swap_buffers;
+
+   psp->getDrawableMSC = dri3_drawable_get_msc;
+   psp->waitForMSC = dri3_wait_for_msc;
+   psp->waitForSBC = dri3_wait_for_sbc;
+   psp->setSwapInterval = dri3_set_swap_interval;
+   psp->getSwapInterval = dri3_get_swap_interval;
+   __glXEnableDirectExtension(&psc->base, "GLX_OML_sync_control");
+
+   psp->copySubBuffer = dri3_copy_sub_buffer;
+   __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
+
+   free(driverName);
+   free(deviceName);
+
+   tmp = getenv("LIBGL_SHOW_FPS");
+   psc->show_fps = tmp && strcmp(tmp, "1") == 0;
+
+   return &psc->base;
+
+handle_error:
+   CriticalErrorMessageF("failed to load driver: %s\n", driverName);
+
+   if (configs)
+       glx_config_destroy_list(configs);
+   if (visuals)
+       glx_config_destroy_list(visuals);
+   if (psc->driScreen)
+       psc->core->destroyScreen(psc->driScreen);
+   psc->driScreen = NULL;
+   if (psc->fd >= 0)
+      close(psc->fd);
+   if (psc->driver)
+      dlclose(psc->driver);
+
+   free(driverName);
+   free(deviceName);
+   glx_screen_cleanup(&psc->base);
+   free(psc);
+
+   return NULL;
+}
+
+/* Called from __glXFreeDisplayPrivate.
+ */
+static void
+dri3_destroy_display(__GLXDRIdisplay * dpy)
+{
+   free(dpy);
+}
+
+/*
+ * Allocate, initialize and return a __DRIdisplayPrivate object.
+ * This is called from __glXInitialize() when we are given a new
+ * display pointer.
+ */
+_X_HIDDEN __GLXDRIdisplay *
+dri3_create_display(Display * dpy)
+{
+   struct dri3_display *pdp;
+   int i;
+
+   pdp = malloc(sizeof *pdp);
+   if (pdp == NULL)
+      return NULL;
+
+   if (!dri3_query_version(dpy, &pdp->dri3Major, &pdp->dri3Minor))
+      goto no_extension;
+
+   if (!present_query_version(dpy, &pdp->presentMajor, &pdp->presentMinor))
+      goto no_extension;
+
+   pdp->base.destroyDisplay = dri3_destroy_display;
+   pdp->base.createScreen = dri3_create_screen;
+
+   i = 0;
+
+   pdp->loader_extensions[i++] = &imageLoaderExtension.base;
+   
+   pdp->loader_extensions[i++] = &systemTimeExtension.base;
+
+   pdp->loader_extensions[i++] = NULL;
+
+   return &pdp->base;
+no_extension:
+   free(pdp);
+   return NULL;
+}
+
+#endif /* GLX_DIRECT_RENDERING */
diff --git a/src/glx/dri3_priv.h b/src/glx/dri3_priv.h
new file mode 100644
index 0000000..2873919
--- /dev/null
+++ b/src/glx/dri3_priv.h
@@ -0,0 +1,199 @@ 
+/*
+ * 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.
+ */
+
+/* This file was derived from dri2_priv.h which carries the following
+ * copyright:
+ *
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Soft-
+ * ware"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, provided that the above copyright
+ * notice(s) and this permission notice appear in all copies of the Soft-
+ * ware and that both the above copyright notice(s) and this permission
+ * notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+ * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
+ * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
+ * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
+ * QUENTIAL 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 PERFOR-
+ * MANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or
+ * other dealings in this Software without prior written authorization of
+ * the copyright holder.
+ *
+ * Authors:
+ *   Kristian Høgsberg (krh@redhat.com)
+ */
+
+#include <xcb/xcb.h>
+#include <xcb/dri3.h>
+#include <xcb/present.h>
+#include <xcb/sync.h>
+
+/* From xmlpool/options.h, user exposed so should be stable */
+#define DRI_CONF_VBLANK_NEVER 0
+#define DRI_CONF_VBLANK_DEF_INTERVAL_0 1
+#define DRI_CONF_VBLANK_DEF_INTERVAL_1 2
+#define DRI_CONF_VBLANK_ALWAYS_SYNC 3
+
+enum dri3_buffer_type {
+   dri3_buffer_back = 0,
+   dri3_buffer_front = 1
+};
+
+struct dri3_buffer {
+   __DRIimage   *image;
+   uint32_t     pixmap;
+   uint32_t     sync_fence;
+   int32_t      *shm_fence;
+   GLboolean    busy;
+   void         *driverPrivate;
+
+   uint32_t     size;
+   uint32_t     pitch;
+   uint32_t     cpp;
+   uint32_t     flags;
+   uint32_t     width, height;
+
+   enum dri3_buffer_type        buffer_type;
+};
+
+struct dri3_display
+{
+   __GLXDRIdisplay base;
+
+   const __DRIextension *loader_extensions[8];
+
+   /* DRI3 bits */
+   int dri3Major;
+   int dri3Minor;
+
+   /* Present bits */
+   int hasPresent;
+   int presentMajor;
+   int presentMinor;
+};
+
+struct dri3_screen {
+   struct glx_screen base;
+
+   __DRIscreen *driScreen;
+   __GLXDRIscreen vtable;
+
+   const __DRIimageExtension *image;
+   const __DRIimageDriverExtension *image_driver;
+   const __DRIcoreExtension *core;
+   const __DRI2flushExtension *f;
+   const __DRI2configQueryExtension *config;
+   const __DRItexBufferExtension *texBuffer;
+   const __DRI2throttleExtension *throttle;
+   const __DRIconfig **driver_configs;
+
+   void *driver;
+   int fd;
+
+   Bool show_fps;
+};
+
+struct dri3_context
+{
+   struct glx_context base;
+   __DRIcontext *driContext;
+};
+
+#define DRI3_MAX_BACK   2
+#define DRI3_BACK_ID(i) (i)
+#define DRI3_FRONT_ID   (DRI3_MAX_BACK)
+
+static inline int
+dri3_buf_id_next(int buf_id)
+{
+   if (buf_id == DRI3_MAX_BACK - 1)
+      return 0;
+   return buf_id + 1;
+}
+
+static inline int
+dri3_buf_id_prev(int buf_id)
+{
+   if (buf_id == 0)
+      return DRI3_MAX_BACK - 1;
+   return buf_id - 1;
+}
+
+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;
+}
+
+struct dri3_drawable
+{
+   __GLXDRIdrawable base;
+   __DRIdrawable *driDrawable;
+   int width, height;
+   int swap_interval;
+   uint8_t have_back;
+   uint8_t have_fake_front;
+   uint8_t is_pixmap;
+
+   uint32_t present_request_serial;
+   uint32_t present_event_serial;
+
+   uint64_t sbc;
+
+   uint64_t ust, msc;
+
+   /* For WaitMSC */
+   uint32_t     present_msc_request_serial;
+   uint32_t     present_msc_event_serial;
+   
+   uint64_t previous_time;
+   unsigned frames;
+
+   struct dri3_buffer *buffers[1 + DRI3_MAX_BACK];
+   int cur_back;
+   int depth;
+
+   uint32_t *stamp;
+
+   xcb_present_event_t eid;
+   xcb_gcontext_t gc;
+   const xcb_query_extension_reply_t *present_extension;
+   xcb_special_event_t *special_event;
+};
+
+char *
+dri3_get_driver_for_fd(int fd);
diff --git a/src/glx/glxclient.h b/src/glx/glxclient.h
index 81ae792..ec168aa 100644
--- a/src/glx/glxclient.h
+++ b/src/glx/glxclient.h
@@ -150,6 +150,7 @@  extern __GLXDRIdisplay *dri2CreateDisplay(Display * dpy);
 extern void dri2InvalidateBuffers(Display *dpy, XID drawable);
 extern unsigned dri2GetSwapEventType(Display *dpy, XID drawable);
 
+extern __GLXDRIdisplay *dri3_create_display(Display * dpy);
 
 /*
 ** Functions to obtain driver configuration information from a direct
@@ -582,6 +583,7 @@  struct glx_display
    __GLXDRIdisplay *driswDisplay;
    __GLXDRIdisplay *driDisplay;
    __GLXDRIdisplay *dri2Display;
+   __GLXDRIdisplay *dri3Display;
 #endif
 };
 
diff --git a/src/glx/glxext.c b/src/glx/glxext.c
index bea1ccb..c6e4d9f 100644
--- a/src/glx/glxext.c
+++ b/src/glx/glxext.c
@@ -770,7 +770,9 @@  AllocAndFetchScreenConfigs(Display * dpy, struct glx_display * priv)
    for (i = 0; i < screens; i++, psc++) {
       psc = NULL;
 #if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
-      if (priv->dri2Display)
+      if (priv->dri3Display)
+         psc = (*priv->dri3Display->createScreen) (i, priv);
+      if (psc == NULL && priv->dri2Display)
 	 psc = (*priv->dri2Display->createScreen) (i, priv);
       if (psc == NULL && priv->driDisplay)
 	 psc = (*priv->driDisplay->createScreen) (i, priv);
@@ -863,6 +865,8 @@  __glXInitialize(Display * dpy)
     ** (e.g., those called in AllocAndFetchScreenConfigs).
     */
    if (glx_direct && glx_accel) {
+      if (!getenv("LIBGL_DRI3_DISABLE"))
+         dpyPriv->dri3Display = dri3_create_display(dpy);
       dpyPriv->dri2Display = dri2CreateDisplay(dpy);
       dpyPriv->driDisplay = driCreateDisplay(dpy);
    }