glx/dri3: Implement LIBGL_SHOW_FPS=1 for DRI3/Present.
[mesa.git] / src / glx / dri3_glx.c
index 70ec057d0b6fb43152e5e46c54904c8b1506fa20..a9ff73b36f4c75ef09fbe26f4272b34eee5342c7 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;
 
@@ -273,8 +271,11 @@ static void
 dri3_update_num_back(struct dri3_drawable *priv)
 {
    priv->num_back = 1;
-   if (priv->flipping)
+   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++;
 }
@@ -360,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;
@@ -394,6 +417,9 @@ dri3_handle_present_event(struct dri3_drawable *priv, xcb_present_generic_event_
             break;
          }
          dri3_update_num_back(priv);
+
+         if (psc->show_fps_interval)
+            show_fps(priv, ce->ust);
       } else {
          priv->recv_msc_serial = ce->serial;
       }
@@ -598,22 +624,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,
@@ -624,7 +672,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,
@@ -657,25 +705,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);
 }
 
 /**
@@ -743,6 +828,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;
@@ -755,11 +841,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
     */
@@ -768,28 +858,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)),
@@ -819,13 +944,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;
 }
 
@@ -845,6 +974,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);
 }
 
@@ -890,6 +1021,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.
        *
@@ -908,6 +1042,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
        */
@@ -937,6 +1073,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);
@@ -1093,13 +1239,13 @@ dri3_find_back(xcb_connection_t *c, struct dri3_drawable *priv)
 
    for (;;) {
       for (b = 0; b < priv->num_back; b++) {
-         int id = DRI3_BACK_ID(b);
+         int id = DRI3_BACK_ID((b + priv->cur_back) % priv->num_back);
          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);
@@ -1120,19 +1266,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;
    }
@@ -1159,14 +1304,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;
@@ -1178,6 +1333,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;
@@ -1240,6 +1406,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;
@@ -1257,7 +1424,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,
@@ -1291,7 +1466,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) {
@@ -1307,9 +1482,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
@@ -1320,11 +1507,12 @@ 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;
 
    unsigned flags = __DRI2_FLUSH_DRAWABLE;
@@ -1332,10 +1520,31 @@ dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
       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
@@ -1344,10 +1553,11 @@ dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
       if (target_msc == 0)
          target_msc = priv->msc + priv->swap_interval * (priv->send_sbc - priv->recv_sbc);
 
-      priv->buffers[buf_id]->busy = 1;
+      back->busy = 1;
+      back->last_swap = priv->send_sbc;
       xcb_present_pixmap(c,
                          priv->base.xDrawable,
-                         priv->buffers[buf_id]->pixmap,
+                         back->pixmap,
                          (uint32_t) priv->send_sbc,
                          0,                                    /* valid */
                          0,                                    /* update */
@@ -1355,7 +1565,7 @@ dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
                          0,                                    /* y_off */
                          None,                                 /* target_crtc */
                          None,
-                         priv->buffers[buf_id]->sync_fence,
+                         back->sync_fence,
                          XCB_PRESENT_OPTION_NONE,
                          target_msc,
                          divisor,
@@ -1367,10 +1577,10 @@ dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor,
        * 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);
@@ -1381,9 +1591,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
@@ -1515,7 +1743,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);
@@ -1523,15 +1752,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
@@ -1564,7 +1793,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");
       }
@@ -1574,18 +1808,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
@@ -1611,7 +1855,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);
@@ -1638,6 +1882,8 @@ 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 = loader_get_driver_for_fd(psc->fd, 0);
@@ -1676,8 +1922,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);
 
@@ -1686,28 +1931,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;
    }
@@ -1715,8 +1959,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;
@@ -1742,9 +1988,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:
@@ -1791,7 +2045,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;
@@ -1847,13 +2100,8 @@ dri3_create_display(Display * dpy)
    pdp->base.createScreen = dri3_create_screen;
 
    loader_set_logger(dri_message);
-   i = 0;
-
-   pdp->loader_extensions[i++] = &imageLoaderExtension.base;
-
-   pdp->loader_extensions[i++] = &systemTimeExtension.base;
 
-   pdp->loader_extensions[i++] = NULL;
+   pdp->loader_extensions = loader_extensions;
 
    return &pdp->base;
 no_extension: