diff mbox

[6/6] Make GLX/dri3 use the Present extension when available

Message ID 1383261196-25093-7-git-send-email-keithp@keithp.com
State Not Applicable
Headers show

Commit Message

Keith Packard Oct. 31, 2013, 11:13 p.m. UTC
This uses the Present extension with DRI3, which includes OML_Sync extension
support.

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 configure.ac                        |   4 +-
 include/GL/internal/dri_interface.h |   1 +
 src/glx/dri3_glx.c                  | 421 ++++++++++++++++++++++--------------
 src/glx/dri3_priv.h                 |  50 ++++-
 4 files changed, 313 insertions(+), 163 deletions(-)
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index b6158d9..c91f031 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,6 +39,7 @@  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
@@ -823,11 +824,12 @@  xyesno)
         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 xcb-dri3 xcb-sync xshmfence"
+    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)
diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
index b06ad8d..b508a18 100644
--- a/include/GL/internal/dri_interface.h
+++ b/include/GL/internal/dri_interface.h
@@ -991,6 +991,7 @@  struct __DRIdri3BufferRec {
    uint32_t pixmap;
    uint32_t sync_fence;
    int32_t *shm_fence;
+   GLboolean busy;
    void *driverPrivate;
 };
 
diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
index 4d275f2..6db984a 100644
--- a/src/glx/dri3_glx.c
+++ b/src/glx/dri3_glx.c
@@ -74,39 +74,24 @@ 
 #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;
 
-#define HAS_SBC 0
-
-#if HAS_SBC
-
-/* For XCB's handling of ust/msc/sbc counters, we have to hand it the high and
- * low halves separately.  This helps you split them.
- */
-static void
-split_counter(uint64_t counter, uint32_t *hi, uint32_t *lo)
-{
-   *hi = (counter >> 32);
-   *lo = counter & 0xffffffff;
-}
-
-static uint64_t
-merge_counter(uint32_t hi, uint32_t lo)
-{
-   return ((uint64_t)hi << 32) | lo;
-}
-#endif /* HAS_SBC */
-
 static inline void
 dri3_fence_reset(xcb_connection_t *c, __DRIdri3Buffer *buffer) {
    xshmfence_reset(buffer->shm_fence);
 }
 
 static inline void
+dri3_fence_set(__DRIdri3Buffer *buffer) {
+   xshmfence_trigger(buffer->shm_fence);
+}
+
+static inline void
 dri3_fence_trigger(xcb_connection_t *c, __DRIdri3Buffer *buffer) {
    xcb_sync_trigger_fence(c, buffer->sync_fence);
 }
@@ -117,6 +102,11 @@  dri3_fence_await(xcb_connection_t *c, __DRIdri3Buffer *buffer) {
    xshmfence_await(buffer->shm_fence);
 }
 
+static inline Bool
+dri3_fence_triggered(__DRIdri3Buffer *buffer) {
+   return xshmfence_query(buffer->shm_fence);
+}
+
 static void
 dri3_destroy_context(struct glx_context *context)
 {
@@ -375,27 +365,44 @@  dri3_create_drawable(struct glx_screen *base, XID xDrawable,
    return &pdraw->base;
 }
 
-#if HAS_SBC
-static int
-dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw,
-                      int64_t *ust, int64_t *msc, int64_t *sbc)
+static void
+present_handle_special_event(struct dri3_drawable *priv, xcb_present_generic_event_t *ge)
 {
-   xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
-   xcb_dri2_get_msc_cookie_t get_msc_cookie;
-   xcb_dri2_get_msc_reply_t *get_msc_reply;
+   switch (ge->evtype) {
+   case XCB_PRESENT_CONFIGURE_NOTIFY: {
+      xcb_present_configure_notify_event_t *ce = (void *) ge;
 
-   get_msc_cookie = xcb_dri2_get_msc_unchecked(c, pdraw->xDrawable);
-   get_msc_reply = xcb_dri2_get_msc_reply(c, get_msc_cookie, NULL);
+      priv->width = ce->width;
+      priv->height = ce->height;
+      break;
+   }
+   case XCB_PRESENT_COMPLETE_NOTIFY: {
+      xcb_present_complete_notify_event_t *ce = (void *) ge;
 
-   if (!get_msc_reply)
-      return 0;
+      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;
 
-   *ust = merge_counter(get_msc_reply->ust_hi, get_msc_reply->ust_lo);
-   *msc = merge_counter(get_msc_reply->msc_hi, get_msc_reply->msc_lo);
-   *sbc = merge_counter(get_msc_reply->sbc_hi, get_msc_reply->sbc_lo);
-   free(get_msc_reply);
+      for (b = 0; b < sizeof (priv->buffers) / sizeof (priv->buffers[0]); b++) {
+         __DRIdri3Buffer        *buf = priv->buffers[b];
 
-   return 1;
+         if (buf && buf->pixmap == ie->pixmap) {
+            buf->busy = 0;
+            break;
+         }
+      }
+      break;
+   }
+   }
+   free(ge);
 }
 
 static int
@@ -403,59 +410,58 @@  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);
-   xcb_dri2_wait_msc_cookie_t wait_msc_cookie;
-   xcb_dri2_wait_msc_reply_t *wait_msc_reply;
-   uint32_t target_msc_hi, target_msc_lo;
-   uint32_t divisor_hi, divisor_lo;
-   uint32_t remainder_hi, remainder_lo;
-
-   split_counter(target_msc, &target_msc_hi, &target_msc_lo);
-   split_counter(divisor, &divisor_hi, &divisor_lo);
-   split_counter(remainder, &remainder_hi, &remainder_lo);
-
-   wait_msc_cookie = xcb_dri2_wait_msc_unchecked(c, pdraw->xDrawable,
-                                                 target_msc_hi, target_msc_lo,
-                                                 divisor_hi, divisor_lo,
-                                                 remainder_hi, remainder_lo);
-   wait_msc_reply = xcb_dri2_wait_msc_reply(c, wait_msc_cookie, NULL);
-
-   if (!wait_msc_reply)
-      return 0;
+   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);
 
-   *ust = merge_counter(wait_msc_reply->ust_hi, wait_msc_reply->ust_lo);
-   *msc = merge_counter(wait_msc_reply->msc_hi, wait_msc_reply->msc_lo);
-   *sbc = merge_counter(wait_msc_reply->sbc_hi, wait_msc_reply->sbc_lo);
-   free(wait_msc_reply);
+   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)
 {
-   xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
-   xcb_dri2_wait_sbc_cookie_t wait_sbc_cookie;
-   xcb_dri2_wait_sbc_reply_t *wait_sbc_reply;
-   uint32_t target_sbc_hi, target_sbc_lo;
-
-   split_counter(target_sbc, &target_sbc_hi, &target_sbc_lo);
-
-   wait_sbc_cookie = xcb_dri2_wait_sbc_unchecked(c, pdraw->xDrawable,
-                                                 target_sbc_hi, target_sbc_lo);
-   wait_sbc_reply = xcb_dri2_wait_sbc_reply(c, wait_sbc_cookie, NULL);
-
-   if (!wait_sbc_reply)
-      return 0;
-
-   *ust = merge_counter(wait_sbc_reply->ust_hi, wait_sbc_reply->ust_lo);
-   *msc = merge_counter(wait_sbc_reply->msc_hi, wait_sbc_reply->msc_lo);
-   *sbc = merge_counter(wait_sbc_reply->sbc_hi, wait_sbc_reply->sbc_lo);
-   free(wait_sbc_reply);
+   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
 
-   return 1;
+   while (priv->sbc < target_sbc) {
+      sleep(1);
+   }
+   return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc, sbc);
 }
-#endif /* HAS_SBC */
 
 static __DRIcontext *
 dri3_get_current_context(void)
@@ -531,13 +537,13 @@  dri3_drawable_gc(struct dri3_drawable *priv)
 static __DRIdri3Buffer *
 dri3_back_buffer(struct dri3_drawable *priv)
 {
-   return priv->buffers[__DRI3_BUFFER_BACK];
+   return priv->buffers[DRI3_BACK_ID(priv->cur_back)];
 }
 
 static __DRIdri3Buffer *
 dri3_fake_front_buffer(struct dri3_drawable *priv)
 {
-   return priv->buffers[__DRI3_BUFFER_FRONT];
+   return priv->buffers[DRI3_FRONT_ID];
 }
 
 static void
@@ -739,6 +745,10 @@  dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, unsigned
    buffer->shm_fence = shm_fence;
    buffer->width = width;
    buffer->height = height;
+
+   /* Mark the buffer as idle */
+   dri3_fence_set(buffer);
+
    return buffer;
    
 no_buffer_fd:
@@ -762,6 +772,26 @@  dri3_free_render_buffer(struct dri3_drawable *pdraw, __DRIdri3Buffer *buffer)
    (*psc->dri3->releaseBuffer)(psc->driScreen, 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)
 {
@@ -781,6 +811,7 @@  dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
                                                 (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) {
@@ -821,30 +852,7 @@  dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
          priv->special_event = NULL;
       }
    }
-
-   /* 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 *pe = (void *) ev;
-
-         switch (pe->evtype) {
-         case XCB_PRESENT_EVENT_CONFIGURE_NOTIFY: {
-            xcb_configure_notify_event_t *ce = (void *) ev;
-
-            priv->width = ce->width;
-            priv->height = ce->height;
-            break;
-         }
-         case XCB_PRESENT_EVENT_IDLE_NOTIFY:
-            break;
-         }
-         free(ev);
-      }
-   }
+   present_flush_events(priv);
    return true;
 }
    
@@ -855,7 +863,8 @@  dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
                        void *loaderPrivate)
 {
    struct dri3_drawable                 *pdraw = loaderPrivate;
-   __DRIdri3Buffer                      *buffer = pdraw->buffers[buffer_type];
+   int                                  buf_id = buffer_type == dri3_pixmap_buf_id(buffer_type);
+   __DRIdri3Buffer                      *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;
@@ -915,7 +924,7 @@  dri3_get_pixmap_buffer(__DRIdrawable *driDrawable,
    buffer->shm_fence = shm_fence;
    buffer->sync_fence = sync_fence;
 
-   pdraw->buffers[buffer_type] = buffer;
+   pdraw->buffers[buf_id] = buffer;
    return buffer;
 
 no_pixmap:
@@ -924,6 +933,32 @@  no_pixmap:
    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);
+         __DRIdri3Buffer        *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 __DRIdri3Buffer *
 dri3_get_buffer(__DRIdrawable *driDrawable,
                 unsigned int format,
@@ -931,10 +966,24 @@  dri3_get_buffer(__DRIdrawable *driDrawable,
                 void *loaderPrivate)
 {
    struct dri3_drawable *priv = loaderPrivate;
-   __DRIdri3Buffer      *buffer = priv->buffers[buffer_type];
+   xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
+   __DRIdri3Buffer      *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) {
-      xcb_connection_t  *c = XGetXCBConnection(priv->base.psc->dpy);
       __DRIdri3Buffer   *new_buffer;
 
       /* Allocate the new buffers
@@ -956,7 +1005,6 @@  dri3_get_buffer(__DRIdrawable *driDrawable,
                            0, 0, 0, 0, priv->width, priv->height);
             dri3_fence_trigger(c, new_buffer);
             dri3_free_render_buffer(priv, buffer);
-            dri3_fence_await(c, new_buffer);
          }
          break;
       case __DRI3_BUFFER_FRONT:
@@ -967,29 +1015,45 @@  dri3_get_buffer(__DRIdrawable *driDrawable,
                         dri3_drawable_gc(priv),
                         0, 0, 0, 0, priv->width, priv->height);
          dri3_fence_trigger(c, new_buffer);
-         dri3_fence_await(c, new_buffer);
          break;
       }
       buffer = new_buffer;
       buffer->buffer_type = buffer_type;
-      priv->buffers[buffer_type] = buffer;
-   }
+      priv->buffers[buf_id] = buffer;
+   } 
+   dri3_fence_await(c, buffer);
 
    /* Return the requested buffer */
    return buffer;
 }
 
 static void
-dri3_free_buffer(__DRIdrawable *driDrawable,
+dri3_free_buffers(__DRIdrawable *driDrawable,
                  enum __DRI3bufferType buffer_type,
                  void *loaderPrivate)
 {
    struct dri3_drawable *priv = loaderPrivate;
-   __DRIdri3Buffer      *buffer = priv->buffers[buffer_type];
+   __DRIdri3Buffer      *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;
+   }
 
-   if (buffer) {
-      dri3_free_render_buffer(priv, buffer);
-      priv->buffers[buffer_type] = NULL;
+   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;
+      }
    }
 }
 
@@ -1030,7 +1094,7 @@  dri3_get_buffers(__DRIdrawable *driDrawable,
          return false;
       priv->have_fake_front = !priv->is_pixmap;
    } else {
-      dri3_free_buffer(driDrawable, __DRI3_BUFFER_FRONT, loaderPrivate);
+      dri3_free_buffers(driDrawable, __DRI3_BUFFER_FRONT, loaderPrivate);
       priv->have_fake_front = 0;
    }
 
@@ -1043,7 +1107,7 @@  dri3_get_buffers(__DRIdrawable *driDrawable,
          return false;
       priv->have_back = 1;
    } else {
-      dri3_free_buffer(driDrawable, __DRI3_BUFFER_BACK, loaderPrivate);
+      dri3_free_buffers(driDrawable, __DRI3_BUFFER_BACK, loaderPrivate);
       priv->have_back = 0;
    }
 
@@ -1059,7 +1123,9 @@  dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
 {
    struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
    struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
-   xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy);
+   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();
@@ -1068,26 +1134,46 @@  dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
       flags |= __DRI2_FLUSH_CONTEXT;
    dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
     
-   if (priv->buffers[0] && !priv->is_pixmap) {
-      dri3_fence_reset(c, priv->buffers[0]);
-      dri3_copy_area(c,
-                     priv->buffers[0]->pixmap,
-                     priv->base.xDrawable,
-                     dri3_drawable_gc(priv),
-                     0, 0, 0, 0, priv->width, priv->height);
-      dri3_fence_trigger(c, priv->buffers[0]);
+   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[1]);
+         dri3_fence_reset(c, priv->buffers[DRI3_FRONT_ID]);
          dri3_copy_area(c,
-                        priv->buffers[0]->pixmap,
-                        priv->buffers[1]->pixmap,
+                        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[1]);
+         dri3_fence_trigger(c, priv->buffers[DRI3_FRONT_ID]);
       }
-      dri3_fence_await(c, priv->buffers[0]);
-      if (priv->have_fake_front)
-         dri3_fence_await(c, priv->buffers[1]);
+      xcb_flush(c);
+      ++ (*psc->dri3->stamp(priv->driDrawable));
    }
 
    return ret;
@@ -1118,6 +1204,30 @@  dri3_query_version(Display *dpy, int *major, int *minor)
 }
 
 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)
@@ -1160,11 +1270,9 @@  dri3_destroy_screen(struct glx_screen *base)
    free(psc);
 }
 
-#if HAS_SBC
 static int
 dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval)
 {
-   xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
    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;
@@ -1186,7 +1294,6 @@  dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval)
       break;
    }
 
-   xcb_dri2_swap_interval(c, priv->base.xDrawable, interval);
    priv->swap_interval = interval;
 
    return 0;
@@ -1199,7 +1306,6 @@  dri3_get_swap_interval(__GLXDRIdrawable *pdraw)
 
   return priv->swap_interval;
 }
-#endif
 
 static const __DRIdri3LoaderExtension dri3LoaderExtension = {
    {__DRI_DRI3_LOADER, __DRI_DRI3_LOADER_VERSION},
@@ -1443,22 +1549,13 @@  dri3_create_screen(int screen, struct glx_display * priv)
    psp->destroyScreen = dri3_destroy_screen;
    psp->createDrawable = dri3_create_drawable;
    psp->swapBuffers = dri3_swap_buffers;
-   psp->getDrawableMSC = NULL;
-   psp->waitForMSC = NULL;
-   psp->waitForSBC = NULL;
-   psp->setSwapInterval = NULL;
-   psp->getSwapInterval = NULL;
-
-#if HAS_SBC
-   if (pdp->driMinor >= 2) {
-      psp->getDrawableMSC = dri3DrawableGetMSC;
-      psp->waitForMSC = dri3WaitForMSC;
-      psp->waitForSBC = dri3WaitForSBC;
-      psp->setSwapInterval = dri3SetSwapInterval;
-      psp->getSwapInterval = dri3GetSwapInterval;
-      __glXEnableDirectExtension(&psc->base, "GLX_OML_sync_control");
-   }
-#endif
+
+   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");
@@ -1517,10 +1614,11 @@  dri3_create_display(Display * dpy)
    if (pdp == NULL)
       return NULL;
 
-   if (!dri3_query_version(dpy, &pdp->dri3Major, &pdp->dri3Minor)) {
-      free(pdp);
-      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;
@@ -1534,6 +1632,9 @@  dri3_create_display(Display * dpy)
    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
index 8703b39..8426253 100644
--- a/src/glx/dri3_priv.h
+++ b/src/glx/dri3_priv.h
@@ -56,6 +56,7 @@ 
 
 #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 */
@@ -73,6 +74,11 @@  struct dri3_display
    /* DRI3 bits */
    int dri3Major;
    int dri3Minor;
+
+   /* Present bits */
+   int hasPresent;
+   int presentMajor;
+   int presentMinor;
 };
 
 struct dri3_screen {
@@ -101,6 +107,35 @@  struct dri3_context
    __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 __DRI3bufferType buffer_type)
+{
+   if (buffer_type == __DRI3_BUFFER_BACK)
+      return DRI3_BACK_ID(0);
+   else
+      return DRI3_FRONT_ID;
+}
+
 struct dri3_drawable
 {
    __GLXDRIdrawable base;
@@ -111,15 +146,26 @@  struct dri3_drawable
    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;
 
-   __DRIdri3Buffer *buffers[2];
+   __DRIdri3Buffer *buffers[1 + DRI3_MAX_BACK];
+   int cur_back;
    int depth;
 
    xcb_present_event_t eid;
    xcb_gcontext_t gc;
-   const xcb_query_extension_reply_t *dri3_extension;
    const xcb_query_extension_reply_t *present_extension;
    xcb_special_event_t *special_event;
 };