glx/dri3: Don't fail on glXSwapBuffersMscOML(dpy, window, 0, 0, 0) (v2)
[mesa.git] / src / glx / dri3_glx.c
index 4aae182199ca7406dd3e93cd75e7c3f576e57b61..1ddc723d5089436a8a6aa4ecadc1148b9e104600 100644 (file)
@@ -65,9 +65,7 @@
 #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/mman.h>
 #include <sys/time.h>
 
-#include "xf86drm.h"
 #include "dri_common.h"
 #include "dri3_priv.h"
+#include "loader.h"
+#include "dri2.h"
 
 static const struct glx_context_vtable dri3_context_vtable;
 
@@ -268,6 +267,19 @@ dri3_create_context(struct glx_screen *base,
 static void
 dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer);
 
+static void
+dri3_update_num_back(struct dri3_drawable *priv)
+{
+   priv->num_back = 1;
+   if (priv->flipping) {
+      if (!priv->is_pixmap && !(priv->present_capabilities & XCB_PRESENT_CAPABILITY_ASYNC))
+         priv->num_back++;
+      priv->num_back++;
+   }
+   if (priv->swap_interval == 0)
+      priv->num_back++;
+}
+
 static void
 dri3_destroy_drawable(__GLXDRIdrawable *base)
 {
@@ -325,6 +337,8 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
       break;
    }
 
+   dri3_update_num_back(pdraw);
+
    (void) __glXInitialize(psc->base.dpy);
 
    /* Create a new drawable */
@@ -347,12 +361,34 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
    return &pdraw->base;
 }
 
+static void
+show_fps(struct dri3_drawable *draw, uint64_t current_ust)
+{
+   const uint64_t interval =
+      ((struct dri3_screen *) draw->base.psc)->show_fps_interval;
+
+   draw->frames++;
+
+   /* DRI3+Present together uses microseconds for UST. */
+   if (draw->previous_ust + interval * 1000000 <= current_ust) {
+      if (draw->previous_ust) {
+         fprintf(stderr, "libGL: FPS = %.1f\n",
+                 ((uint64_t) draw->frames * 1000000) /
+                 (double)(current_ust - draw->previous_ust));
+      }
+      draw->frames = 0;
+      draw->previous_ust = current_ust;
+   }
+}
+
 /*
  * Process one Present event
  */
 static void
 dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_t *ge)
 {
+   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
+
    switch (ge->evtype) {
    case XCB_PRESENT_CONFIGURE_NOTIFY: {
       xcb_present_configure_notify_event_t *ce = (void *) ge;
@@ -364,12 +400,34 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_
    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;
+      /* Compute the processed SBC number from the received 32-bit serial number merged
+       * with the upper 32-bits of the sent 64-bit serial number while checking for
+       * wrap
+       */
+      if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {
+         priv->recv_sbc = (priv->send_sbc & 0xffffffff00000000LL) | ce->serial;
+         if (priv->recv_sbc > priv->send_sbc)
+            priv->recv_sbc -= 0x100000000;
+         switch (ce->mode) {
+         case XCB_PRESENT_COMPLETE_MODE_FLIP:
+            priv->flipping = true;
+            break;
+         case XCB_PRESENT_COMPLETE_MODE_COPY:
+            priv->flipping = false;
+            break;
+         }
+         dri3_update_num_back(priv);
+
+         if (psc->show_fps_interval)
+            show_fps(priv, ce->ust);
+
+         priv->ust = ce->ust;
+         priv->msc = ce->msc;
+      } else {
+         priv->recv_msc_serial = ce->serial;
+         priv->notify_ust = ce->ust;
+         priv->notify_msc = ce->msc;
+      }
       break;
    }
    case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
@@ -381,6 +439,10 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_
 
          if (buf && buf->pixmap == ie->pixmap) {
             buf->busy = 0;
+            if (priv->num_back <= b && b < DRI3_MAX_BACK) {
+               dri3_free_render_buffer(priv, buf);
+               priv->buffers[b] = NULL;
+            }
             break;
          }
       }
@@ -390,20 +452,41 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_
    free(ge);
 }
 
+static bool
+dri3_wait_for_event(__GLXDRIdrawable *pdraw)
+{
+   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;
+
+   xcb_flush(c);
+   ev = xcb_wait_for_special_event(c, priv->special_event);
+   if (!ev)
+      return false;
+   ge = (void *) ev;
+   dri3_handle_present_event(priv, ge);
+   return true;
+}
+
+/** dri3_wait_for_msc
+ *
+ * Get the X server to send an event when the target msc/divisor/remainder is
+ * reached.
+ */
 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;
+   uint32_t msc_serial;
 
    /* Ask for the an event for the target MSC */
-   ++priv->present_msc_request_serial;
+   msc_serial = ++priv->send_msc_serial;
    xcb_present_notify_msc(c,
                           priv->base.xDrawable,
-                          priv->present_msc_request_serial,
+                          msc_serial,
                           target_msc,
                           divisor,
                           remainder);
@@ -412,22 +495,24 @@ dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
 
    /* 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;
-         dri3_handle_present_event(priv, ge);
+      while ((int32_t) (msc_serial - priv->recv_msc_serial) > 0) {
+         if (!dri3_wait_for_event(pdraw))
+            return 0;
       }
    }
 
-   *ust = priv->ust;
-   *msc = priv->msc;
-   *sbc = priv->sbc;
+   *ust = priv->notify_ust;
+   *msc = priv->notify_msc;
+   *sbc = priv->recv_sbc;
 
    return 1;
 }
 
+/** dri3_drawable_get_msc
+ *
+ * Return the current UST/MSC/SBC triplet by asking the server
+ * for an event
+ */
 static int
 dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw,
                       int64_t *ust, int64_t *msc, int64_t *sbc)
@@ -437,12 +522,9 @@ dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw,
 
 /** 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
+ * Wait for the completed swap buffer count to reach the specified
+ * target. Presumably the application knows that this will be reached with
+ * outstanding complete events, or we're going to be here awhile.
  */
 static int
 dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust,
@@ -450,10 +532,24 @@ dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust,
 {
    struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
 
-   while (priv->sbc < target_sbc) {
-      sleep(1);
+   /* From the GLX_OML_sync_control spec:
+    *
+    *     "If <target_sbc> = 0, the function will block until all previous
+    *      swaps requested with glXSwapBuffersMscOML for that window have
+    *      completed."
+    */
+   if (!target_sbc)
+      target_sbc = priv->send_sbc;
+
+   while (priv->recv_sbc < target_sbc) {
+      if (!dri3_wait_for_event(pdraw))
+         return 0;
    }
-   return dri3_wait_for_msc(pdraw, 0, 0, 0, ust, msc, sbc);
+
+   *ust = priv->ust;
+   *msc = priv->msc;
+   *sbc = priv->recv_sbc;
+   return 1;
 }
 
 /**
@@ -540,22 +636,44 @@ dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
 {
    struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
    struct dri3_screen *psc = (struct dri3_screen *) pdraw->psc;
+   struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext();
    xcb_connection_t     *c = XGetXCBConnection(priv->base.psc->dpy);
-   struct dri3_buffer *back = dri3_back_buffer(priv);
+   struct dri3_buffer *back;
 
-   unsigned flags;
+   unsigned flags = __DRI2_FLUSH_DRAWABLE;
 
    /* 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, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
 
+   back = dri3_back_buffer(priv);
    y = priv->height - y - height;
 
+   if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) {
+      /* Update the linear buffer part of the back buffer
+       * for the dri3_copy_area operation
+       */
+      psc->image->blitImage(pcp->driContext,
+                            back->linear_buffer,
+                            back->image,
+                            0, 0, back->width,
+                            back->height,
+                            0, 0, back->width,
+                            back->height, __BLIT_FLAG_FLUSH);
+      /* We use blitImage to update our fake front,
+       */
+      if (priv->have_fake_front)
+         psc->image->blitImage(pcp->driContext,
+                               dri3_fake_front_buffer(priv)->image,
+                               back->image,
+                               x, y, width, height,
+                               x, y, width, height, __BLIT_FLAG_FLUSH);
+   }
+
    dri3_fence_reset(c, back);
    dri3_copy_area(c,
                   dri3_back_buffer(priv)->pixmap,
@@ -566,7 +684,7 @@ dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y,
    /* Refresh the fake front (if present) after we just damaged the real
     * front.
     */
-   if (priv->have_fake_front) {
+   if (priv->have_fake_front && !psc->is_different_gpu) {
       dri3_fence_reset(c, dri3_fake_front_buffer(priv));
       dri3_copy_area(c,
                      dri3_back_buffer(priv)->pixmap,
@@ -599,25 +717,62 @@ dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src)
 static void
 dri3_wait_x(struct glx_context *gc)
 {
+   struct dri3_context *pcp = (struct dri3_context *) gc;
    struct dri3_drawable *priv = (struct dri3_drawable *)
       GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
+   struct dri3_screen *psc;
+   struct dri3_buffer *front;
 
    if (priv == NULL || !priv->have_fake_front)
       return;
 
-   dri3_copy_drawable(priv, dri3_fake_front_buffer(priv)->pixmap, priv->base.xDrawable);
+   psc = (struct dri3_screen *) priv->base.psc;
+   front = dri3_fake_front_buffer(priv);
+
+   dri3_copy_drawable(priv, front->pixmap, priv->base.xDrawable);
+
+   /* In the psc->is_different_gpu case, the linear buffer has been updated,
+    * but not yet the tiled buffer.
+    * Copy back to the tiled buffer we use for rendering.
+    * Note that we don't need flushing.
+    */
+   if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base)
+      psc->image->blitImage(pcp->driContext,
+                            front->image,
+                            front->linear_buffer,
+                            0, 0, front->width,
+                            front->height,
+                            0, 0, front->width,
+                            front->height, 0);
 }
 
 static void
 dri3_wait_gl(struct glx_context *gc)
 {
+   struct dri3_context *pcp = (struct dri3_context *) gc;
    struct dri3_drawable *priv = (struct dri3_drawable *)
       GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable);
+   struct dri3_screen *psc;
+   struct dri3_buffer *front;
 
    if (priv == NULL || !priv->have_fake_front)
       return;
 
-   dri3_copy_drawable(priv, priv->base.xDrawable, dri3_fake_front_buffer(priv)->pixmap);
+   psc = (struct dri3_screen *) priv->base.psc;
+   front = dri3_fake_front_buffer(priv);
+
+   /* In the psc->is_different_gpu case, we update the linear_buffer
+    * before updating the real front.
+    */
+   if (psc->is_different_gpu && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base)
+      psc->image->blitImage(pcp->driContext,
+                            front->linear_buffer,
+                            front->image,
+                            0, 0, front->width,
+                            front->height,
+                            0, 0, front->width,
+                            front->height, __BLIT_FLAG_FLUSH);
+   dri3_copy_drawable(priv, priv->base.xDrawable, front->pixmap);
 }
 
 /**
@@ -685,6 +840,7 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw,
    struct dri3_screen *psc = (struct dri3_screen *) glx_screen;
    Display *dpy = glx_screen->dpy;
    struct dri3_buffer *buffer;
+   __DRIimage *pixmap_buffer;
    xcb_connection_t *c = XGetXCBConnection(dpy);
    xcb_pixmap_t pixmap;
    xcb_sync_fence_t sync_fence;
@@ -697,11 +853,15 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw,
     */
 
    fence_fd = xshmfence_alloc_shm();
-   if (fence_fd < 0)
+   if (fence_fd < 0) {
+      ErrorMessageF("DRI3 Fence object allocation failure %s\n", strerror(errno));
       return NULL;
+   }
    shm_fence = xshmfence_map_shm(fence_fd);
-   if (shm_fence == NULL)
+   if (shm_fence == NULL) {
+      ErrorMessageF("DRI3 Fence object map failure %s\n", strerror(errno));
       goto no_shm_fence;
+   }
 
    /* Allocate the image from the driver
     */
@@ -710,28 +870,63 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw,
       goto no_buffer;
 
    buffer->cpp = dri3_cpp_for_format(format);
-   if (!buffer->cpp)
+   if (!buffer->cpp) {
+      ErrorMessageF("DRI3 buffer format %d invalid\n", format);
       goto no_image;
+   }
 
-   buffer->image = (*psc->image->createImage) (psc->driScreen,
-                                               width, height,
-                                               format,
-                                               __DRI_IMAGE_USE_SHARE|__DRI_IMAGE_USE_SCANOUT,
-                                               buffer);
-
+   if (!psc->is_different_gpu) {
+      buffer->image = (*psc->image->createImage) (psc->driScreen,
+                                                  width, height,
+                                                  format,
+                                                  __DRI_IMAGE_USE_SHARE |
+                                                  __DRI_IMAGE_USE_SCANOUT,
+                                                  buffer);
+      pixmap_buffer = buffer->image;
+
+      if (!buffer->image) {
+         ErrorMessageF("DRI3 gpu image creation failure\n");
+         goto no_image;
+      }
+   } else {
+      buffer->image = (*psc->image->createImage) (psc->driScreen,
+                                                  width, height,
+                                                  format,
+                                                  0,
+                                                  buffer);
+
+      if (!buffer->image) {
+         ErrorMessageF("DRI3 other gpu image creation failure\n");
+         goto no_image;
+      }
 
-   if (!buffer->image)
-      goto no_image;
+      buffer->linear_buffer = (*psc->image->createImage) (psc->driScreen,
+                                                          width, height,
+                                                          format,
+                                                          __DRI_IMAGE_USE_SHARE |
+                                                          __DRI_IMAGE_USE_LINEAR,
+                                                          buffer);
+      pixmap_buffer = buffer->linear_buffer;
+
+      if (!buffer->linear_buffer) {
+         ErrorMessageF("DRI3 gpu linear image creation failure\n");
+         goto no_linear_buffer;
+      }
+   }
 
    /* X wants the stride, so ask the image for it
     */
-   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_STRIDE, &stride))
+   if (!(*psc->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE, &stride)) {
+      ErrorMessageF("DRI3 get image stride failed\n");
       goto no_buffer_attrib;
+   }
 
    buffer->pitch = stride;
 
-   if (!(*psc->image->queryImage)(buffer->image, __DRI_IMAGE_ATTRIB_FD, &buffer_fd))
+   if (!(*psc->image->queryImage)(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD, &buffer_fd)) {
+      ErrorMessageF("DRI3 get image FD failed\n");
       goto no_buffer_attrib;
+   }
 
    xcb_dri3_pixmap_from_buffer(c,
                                (pixmap = xcb_generate_id(c)),
@@ -761,13 +956,17 @@ dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw,
    return buffer;
 
 no_buffer_attrib:
-   (*psc->image->destroyImage)(buffer->image);
+   (*psc->image->destroyImage)(pixmap_buffer);
+no_linear_buffer:
+   if (psc->is_different_gpu)
+      (*psc->image->destroyImage)(buffer->image);
 no_image:
    free(buffer);
 no_buffer:
    xshmfence_unmap_shm(shm_fence);
 no_shm_fence:
    close(fence_fd);
+   ErrorMessageF("DRI3 alloc_render_buffer failed\n");
    return NULL;
 }
 
@@ -787,6 +986,8 @@ dri3_free_render_buffer(struct dri3_drawable *pdraw, struct dri3_buffer *buffer)
    xcb_sync_destroy_fence(c, buffer->sync_fence);
    xshmfence_unmap_shm(buffer->shm_fence);
    (*psc->image->destroyImage)(buffer->image);
+   if (buffer->linear_buffer)
+      (*psc->image->destroyImage)(buffer->linear_buffer);
    free(buffer);
 }
 
@@ -832,6 +1033,9 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
       xcb_get_geometry_reply_t                  *geom_reply;
       xcb_void_cookie_t                         cookie;
       xcb_generic_error_t                       *error;
+      xcb_present_query_capabilities_cookie_t   present_capabilities_cookie;
+      xcb_present_query_capabilities_reply_t    *present_capabilities_reply;
+
 
       /* Try to select for input on the window.
        *
@@ -850,6 +1054,8 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
                                                 XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY|
                                                 XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
 
+      present_capabilities_cookie = xcb_present_query_capabilities(c, priv->base.xDrawable);
+
       /* Create an XCB event queue to hold present events outside of the usual
        * application event queue
        */
@@ -879,6 +1085,16 @@ dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate)
 
       error = xcb_request_check(c, cookie);
 
+      present_capabilities_reply = xcb_present_query_capabilities_reply(c,
+                                                                        present_capabilities_cookie,
+                                                                        NULL);
+
+      if (present_capabilities_reply) {
+         priv->present_capabilities = present_capabilities_reply->capabilities;
+         free(present_capabilities_reply);
+      } else
+         priv->present_capabilities = 0;
+
       if (error) {
          if (error->error_code != BadWindow) {
             free(error);
@@ -904,6 +1120,7 @@ image_format_to_fourcc(int format)
 
    /* Convert from __DRI_IMAGE_FORMAT to __DRI_IMAGE_FOURCC (sigh) */
    switch (format) {
+   case __DRI_IMAGE_FORMAT_SARGB8: return __DRI_IMAGE_FOURCC_SARGB8888;
    case __DRI_IMAGE_FORMAT_RGB565: return __DRI_IMAGE_FOURCC_RGB565;
    case __DRI_IMAGE_FORMAT_XRGB8888: return __DRI_IMAGE_FOURCC_XRGB8888;
    case __DRI_IMAGE_FORMAT_ARGB8888: return __DRI_IMAGE_FOURCC_ARGB8888;
@@ -1033,16 +1250,16 @@ dri3_find_back(xcb_connection_t *c, struct dri3_drawable *priv)
    xcb_present_generic_event_t *ge;
 
    for (;;) {
+      for (b = 0; b < priv->num_back; b++) {
+         int id = DRI3_BACK_ID((b + priv->cur_back) % priv->num_back);
+         struct dri3_buffer *buffer = priv->buffers[id];
 
-      for (b = 0; b < DRI3_NUM_BACK; b++) {
-         int                    id = DRI3_BACK_ID(b);
-         struct dri3_buffer        *buffer = priv->buffers[id];
-
-         if (!buffer)
-            return b;
-         if (!buffer->busy)
-            return b;
+         if (!buffer || !buffer->busy) {
+            priv->cur_back = id;
+            return id;
+         }
       }
+      xcb_flush(c);
       ev = xcb_wait_for_special_event(c, priv->special_event);
       if (!ev)
          return -1;
@@ -1061,19 +1278,18 @@ dri3_get_buffer(__DRIdrawable *driDrawable,
                 enum dri3_buffer_type buffer_type,
                 void *loaderPrivate)
 {
+   struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext();
    struct dri3_drawable *priv = loaderPrivate;
+   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
    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);
+      buf_id = dri3_find_back(c, priv);
 
-      if (back < 0)
+      if (buf_id < 0)
          return NULL;
-
-      priv->cur_back = back;
-      buf_id = DRI3_BACK_ID(priv->cur_back);
    } else {
       buf_id = DRI3_FRONT_ID;
    }
@@ -1100,14 +1316,24 @@ dri3_get_buffer(__DRIdrawable *driDrawable,
       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);
+            if (!buffer->linear_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);
+            } else if ((&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) {
+               psc->image->blitImage(pcp->driContext,
+                                     new_buffer->image,
+                                     buffer->image,
+                                     0, 0, priv->width,
+                                     priv->height,
+                                     0, 0, priv->width,
+                                     priv->height, 0);
+            }
             dri3_free_render_buffer(priv, buffer);
          }
          break;
@@ -1119,6 +1345,17 @@ dri3_get_buffer(__DRIdrawable *driDrawable,
                         dri3_drawable_gc(priv),
                         0, 0, 0, 0, priv->width, priv->height);
          dri3_fence_trigger(c, new_buffer);
+
+         if (new_buffer->linear_buffer && (&pcp->base != &dummyContext) && pcp->base.psc == &psc->base) {
+            dri3_fence_await(c, new_buffer);
+            psc->image->blitImage(pcp->driContext,
+                                  new_buffer->image,
+                                  new_buffer->linear_buffer,
+                                  0, 0, priv->width,
+                                  priv->height,
+                                  0, 0, priv->width,
+                                  priv->height, 0);
+         }
          break;
       }
       buffer = new_buffer;
@@ -1150,7 +1387,7 @@ dri3_free_buffers(__DRIdrawable *driDrawable,
    switch (buffer_type) {
    case dri3_buffer_back:
       first_id = DRI3_BACK_ID(0);
-      n_id = DRI3_NUM_BACK;
+      n_id = DRI3_MAX_BACK;
       break;
    case dri3_buffer_front:
       first_id = DRI3_FRONT_ID;
@@ -1181,6 +1418,7 @@ dri3_get_buffers(__DRIdrawable *driDrawable,
                  struct __DRIimageList *buffers)
 {
    struct dri3_drawable *priv = loaderPrivate;
+   struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc;
    struct dri3_buffer   *front, *back;
 
    buffers->image_mask = 0;
@@ -1198,7 +1436,15 @@ dri3_get_buffers(__DRIdrawable *driDrawable,
       buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
 
    if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
-      if (priv->is_pixmap)
+      /* All pixmaps are owned by the server gpu.
+       * When we use a different gpu, we can't use the pixmap
+       * as buffer since it is potentially tiled a way
+       * our device can't understand. In this case, use
+       * a fake front buffer. Hopefully the pixmap
+       * content will get synced with the fake front
+       * buffer.
+       */
+      if (priv->is_pixmap && !psc->is_different_gpu)
          front = dri3_get_pixmap_buffer(driDrawable,
                                         format,
                                         dri3_buffer_front,
@@ -1232,7 +1478,7 @@ dri3_get_buffers(__DRIdrawable *driDrawable,
    if (front) {
       buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
       buffers->front = front->image;
-      priv->have_fake_front = !priv->is_pixmap;
+      priv->have_fake_front = psc->is_different_gpu || !priv->is_pixmap;
    }
 
    if (back) {
@@ -1248,9 +1494,21 @@ dri3_get_buffers(__DRIdrawable *driDrawable,
 /* The image loader extension record for DRI3
  */
 static const __DRIimageLoaderExtension imageLoaderExtension = {
-   {__DRI_IMAGE_LOADER, __DRI_IMAGE_LOADER_VERSION},
-   .getBuffers = dri3_get_buffers,
-   .flushFrontBuffer = dri3_flush_front_buffer,
+   .base = { __DRI_IMAGE_LOADER, 1 },
+
+   .getBuffers          = dri3_get_buffers,
+   .flushFrontBuffer    = dri3_flush_front_buffer,
+};
+
+const __DRIuseInvalidateExtension dri3UseInvalidate = {
+   .base = { __DRI_USE_INVALIDATE, 1 }
+};
+
+static const __DRIextension *loader_extensions[] = {
+   &imageLoaderExtension.base,
+   &systemTimeExtension.base,
+   &dri3UseInvalidate.base,
+   NULL
 };
 
 /** dri3_swap_buffers
@@ -1261,57 +1519,105 @@ static int64_t
 dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
                   int64_t remainder, Bool flush)
 {
+   struct dri3_context *pcp = (struct dri3_context *) __glXGetCurrentContext();
    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);
+   struct dri3_buffer *back;
    int64_t ret = 0;
+   uint32_t options = XCB_PRESENT_OPTION_NONE;
 
    unsigned flags = __DRI2_FLUSH_DRAWABLE;
    if (flush)
       flags |= __DRI2_FLUSH_CONTEXT;
    dri3_flush(psc, priv, flags, __DRI2_THROTTLE_SWAPBUFFER);
 
+   back = priv->buffers[DRI3_BACK_ID(priv->cur_back)];
+   if (psc->is_different_gpu && back) {
+      /* Update the linear buffer before presenting the pixmap */
+      psc->image->blitImage(pcp->driContext,
+                            back->linear_buffer,
+                            back->image,
+                            0, 0, back->width,
+                            back->height,
+                            0, 0, back->width,
+                            back->height, __BLIT_FLAG_FLUSH);
+      /* Update the fake front */
+      if (priv->have_fake_front)
+         psc->image->blitImage(pcp->driContext,
+                               priv->buffers[DRI3_FRONT_ID]->image,
+                               back->image,
+                               0, 0, priv->width,
+                               priv->height,
+                               0, 0, priv->width,
+                               priv->height, __BLIT_FLAG_FLUSH);
+   }
+
    dri3_flush_present_events(priv);
 
-   if (priv->buffers[buf_id] && !priv->is_pixmap) {
-      dri3_fence_reset(c, priv->buffers[buf_id]);
+   if (back && !priv->is_pixmap) {
+      dri3_fence_reset(c, back);
 
       /* Compute when we want the frame shown by taking the last known successful
-       * MSC and adding in a swap interval for each outstanding swap request
+       * MSC and adding in a swap interval for each outstanding swap request.
+       * target_msc=divisor=remainder=0 means "Use glXSwapBuffers() semantic"
        */
-      ++priv->present_request_serial;
-      if (target_msc == 0)
-         target_msc = priv->msc + priv->swap_interval * (priv->present_request_serial - priv->present_event_serial);
+      ++priv->send_sbc;
+      if (target_msc == 0 && divisor == 0 && remainder == 0)
+         target_msc = priv->msc + priv->swap_interval * (priv->send_sbc - priv->recv_sbc);
+      else if (divisor == 0 && remainder > 0) {
+         /* From the GLX_OML_sync_control spec:
+          *
+          *     "If <divisor> = 0, the swap will occur when MSC becomes
+          *      greater than or equal to <target_msc>."
+          *
+          * Note that there's no mention of the remainder.  The Present extension
+          * throws BadValue for remainder != 0 with divisor == 0, so just drop
+          * the passed in value.
+          */
+         remainder = 0;
+      }
 
-      priv->buffers[buf_id]->busy = 1;
+      /* From the GLX_EXT_swap_control spec:
+       *
+       *     "If <interval> is set to a value of 0, buffer swaps are not
+       *      synchronized to a video frame."
+       *
+       * Implementation note: It is possible to enable triple buffering behaviour
+       * by not using XCB_PRESENT_OPTION_ASYNC, but this should not be the default.
+       */
+      if (priv->swap_interval == 0)
+          options |= XCB_PRESENT_OPTION_ASYNC;
+
+      back->busy = 1;
+      back->last_swap = priv->send_sbc;
       xcb_present_pixmap(c,
                          priv->base.xDrawable,
-                         priv->buffers[buf_id]->pixmap,
-                         priv->present_request_serial,
+                         back->pixmap,
+                         (uint32_t) priv->send_sbc,
                          0,                                    /* valid */
                          0,                                    /* update */
                          0,                                    /* x_off */
                          0,                                    /* y_off */
                          None,                                 /* target_crtc */
                          None,
-                         priv->buffers[buf_id]->sync_fence,
-                         XCB_PRESENT_OPTION_NONE,
+                         back->sync_fence,
+                         options,
                          target_msc,
                          divisor,
                          remainder, 0, NULL);
-      ret = ++priv->sbc;
+      ret = (int64_t) priv->send_sbc;
 
       /* If there's a fake front, then copy the source back buffer
        * to the fake front to keep it up to date. This needs
        * to reset the fence and make future users block until
        * the X server is done copying the bits
        */
-      if (priv->have_fake_front) {
+      if (priv->have_fake_front && !psc->is_different_gpu) {
          dri3_fence_reset(c, priv->buffers[DRI3_FRONT_ID]);
          dri3_copy_area(c,
-                        priv->buffers[buf_id]->pixmap,
+                        back->pixmap,
                         priv->buffers[DRI3_FRONT_ID]->pixmap,
                         dri3_drawable_gc(priv),
                         0, 0, 0, 0, priv->width, priv->height);
@@ -1322,9 +1628,27 @@ dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
          ++(*priv->stamp);
    }
 
+   (*psc->f->invalidate)(priv->driDrawable);
+
    return ret;
 }
 
+static int
+dri3_get_buffer_age(__GLXDRIdrawable *pdraw)
+{
+   xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy);
+   struct dri3_drawable *priv = (struct dri3_drawable *) pdraw;
+   int back_id = DRI3_BACK_ID(dri3_find_back(c, priv));
+
+   if (back_id < 0 || !priv->buffers[back_id])
+      return 0;
+
+   if (priv->buffers[back_id]->last_swap != 0)
+      return priv->send_sbc - priv->buffers[back_id]->last_swap + 1;
+   else
+      return 0;
+}
+
 /** dri3_open
  *
  * Wrapper around xcb_dri3_open
@@ -1337,14 +1661,13 @@ dri3_open(Display *dpy,
    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);
+   reply = xcb_dri3_open_reply(c, cookie, NULL);
    if (!reply)
       return -1;
 
@@ -1403,6 +1726,7 @@ dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval)
    }
 
    priv->swap_interval = interval;
+   dri3_update_num_back(priv);
 
    return 0;
 }
@@ -1456,7 +1780,8 @@ dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
    if (pdraw != NULL) {
       psc = (struct dri3_screen *) base->psc;
 
-      if (psc->texBuffer->releaseTexBuffer)
+      if (psc->texBuffer->base.version >= 3 &&
+          psc->texBuffer->releaseTexBuffer != NULL)
          (*psc->texBuffer->releaseTexBuffer) (pcp->driContext,
                                               pdraw->base.textureTarget,
                                               pdraw->driDrawable);
@@ -1464,15 +1789,15 @@ dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
 }
 
 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 */
+   .destroy             = dri3_destroy_context,
+   .bind                = dri3_bind_context,
+   .unbind              = dri3_unbind_context,
+   .wait_gl             = dri3_wait_gl,
+   .wait_x              = dri3_wait_x,
+   .use_x_font          = DRI_glXUseXFont,
+   .bind_tex_image      = dri3_bind_tex_image,
+   .release_tex_image   = dri3_release_tex_image,
+   .get_proc_address    = NULL,
 };
 
 /** dri3_bind_extensions
@@ -1493,23 +1818,7 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
    __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");
-//   }
+   __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event");
 
    mask = psc->image_driver->getAPIMask(psc->driScreen);
 
@@ -1521,7 +1830,12 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
                                  "GLX_EXT_create_context_es2_profile");
 
    for (i = 0; extensions[i]; i++) {
-      if ((strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
+      /* when on a different gpu than the server, the server pixmaps
+       * can have a tiling mode we can't read. Thus we can't create
+       * a texture from them.
+       */
+      if (!psc->is_different_gpu &&
+         (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
          psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
          __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
       }
@@ -1531,18 +1845,28 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
          /* internal driver extension, no GL extension exposed */
       }
 
+      if (strcmp(extensions[i]->name, __DRI_IMAGE) == 0)
+         psc->image = (__DRIimageExtension *) extensions[i];
+
       if ((strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0))
          psc->config = (__DRI2configQueryExtension *) extensions[i];
 
       if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0)
          __glXEnableDirectExtension(&psc->base,
                                     "GLX_ARB_create_context_robustness");
+
+      if (strcmp(extensions[i]->name, __DRI2_RENDERER_QUERY) == 0) {
+         psc->rendererQuery = (__DRI2rendererQueryExtension *) extensions[i];
+         __glXEnableDirectExtension(&psc->base, "GLX_MESA_query_renderer");
+      }
    }
 }
 
 static const struct glx_screen_vtable dri3_screen_vtable = {
-   dri3_create_context,
-   dri3_create_context_attribs
+   .create_context         = dri3_create_context,
+   .create_context_attribs = dri3_create_context_attribs,
+   .query_renderer_integer = dri3_query_renderer_integer,
+   .query_renderer_string  = dri3_query_renderer_string,
 };
 
 /** dri3_create_screen
@@ -1568,7 +1892,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
    struct dri3_screen *psc;
    __GLXDRIscreen *psp;
    struct glx_config *configs = NULL, *visuals = NULL;
-   char *driverName, *deviceName;
+   char *driverName, *deviceName, *tmp;
    int i;
 
    psc = calloc(1, sizeof *psc);
@@ -1595,9 +1919,11 @@ dri3_create_screen(int screen, struct glx_display * priv)
 
       return NULL;
    }
+
+   psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu);
    deviceName = NULL;
 
-   driverName = dri3_get_driver_for_fd(psc->fd);
+   driverName = loader_get_driver_for_fd(psc->fd, 0);
    if (!driverName) {
       ErrorMessageF("No driver found\n");
       goto handle_error;
@@ -1633,8 +1959,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
 
    psc->driScreen =
       psc->image_driver->createNewScreen2(screen, psc->fd,
-                                          (const __DRIextension **)
-                                          &pdp->loader_extensions[0],
+                                          pdp->loader_extensions,
                                           extensions,
                                           &driver_configs, psc);
 
@@ -1643,28 +1968,27 @@ dri3_create_screen(int screen, struct glx_display * priv)
       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];
-   }
+   dri3_bind_extensions(psc, priv, driverName);
 
-   if (psc->image == NULL) {
-      ErrorMessageF("image extension not found\n");
+   if (!psc->image || psc->image->base.version < 7 || !psc->image->createImageFromFds) {
+      ErrorMessageF("Version 7 or imageFromFds image extension not found\n");
       goto handle_error;
    }
 
-   dri3_bind_extensions(psc, priv, driverName);
-
    if (!psc->f || psc->f->base.version < 4) {
       ErrorMessageF("Version 4 or later of flush extension not found\n");
       goto handle_error;
    }
 
-   if (!psc->texBuffer || psc->texBuffer->base.version < 2 ||
-       !psc->texBuffer->setTexBuffer2)
-   {
+   if (psc->is_different_gpu && psc->image->base.version < 9) {
+      ErrorMessageF("Different GPU, but image extension version 9 or later not found\n");
+      goto handle_error;
+   }
+
+   if (!psc->is_different_gpu && (
+       !psc->texBuffer || psc->texBuffer->base.version < 2 ||
+       !psc->texBuffer->setTexBuffer2
+       )) {
       ErrorMessageF("Version 2 or later of texBuffer extension not found\n");
       goto handle_error;
    }
@@ -1672,8 +1996,10 @@ dri3_create_screen(int screen, struct glx_display * priv)
    configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs);
    visuals = driConvertConfigs(psc->core, psc->base.visuals, driver_configs);
 
-   if (!configs || !visuals)
+   if (!configs || !visuals) {
+       ErrorMessageF("No matching fbConfigs or visuals found\n");
        goto handle_error;
+   }
 
    glx_config_destroy_list(psc->base.configs);
    psc->base.configs = configs;
@@ -1699,9 +2025,17 @@ dri3_create_screen(int screen, struct glx_display * priv)
    psp->copySubBuffer = dri3_copy_sub_buffer;
    __glXEnableDirectExtension(&psc->base, "GLX_MESA_copy_sub_buffer");
 
+   psp->getBufferAge = dri3_get_buffer_age;
+   __glXEnableDirectExtension(&psc->base, "GLX_EXT_buffer_age");
+
    free(driverName);
    free(deviceName);
 
+   tmp = getenv("LIBGL_SHOW_FPS");
+   psc->show_fps_interval = tmp ? atoi(tmp) : 0;
+   if (psc->show_fps_interval < 0)
+      psc->show_fps_interval = 0;
+
    return &psc->base;
 
 handle_error:
@@ -1748,7 +2082,6 @@ _X_HIDDEN __GLXDRIdisplay *
 dri3_create_display(Display * dpy)
 {
    struct dri3_display                  *pdp;
-   int                                  i;
    xcb_connection_t                     *c = XGetXCBConnection(dpy);
    xcb_dri3_query_version_cookie_t      dri3_cookie;
    xcb_dri3_query_version_reply_t       *dri3_reply;
@@ -1798,17 +2131,14 @@ dri3_create_display(Display * dpy)
    }
    pdp->presentMajor = present_reply->major_version;
    pdp->presentMinor = present_reply->minor_version;
+   free(present_reply);
 
    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;
+   loader_set_logger(dri_message);
 
-   pdp->loader_extensions[i++] = NULL;
+   pdp->loader_extensions = loader_extensions;
 
    return &pdp->base;
 no_extension: