[Mesa-dev] [PATCH 2/2] dri3: Swapbuffer update

Thomas Hellstrom thellstrom at vmware.com
Tue Aug 8 06:48:36 UTC 2017


Implement back-to-fake-front flips,
Fix EGL_BUFFER_PRESERVED path.
Implement dri3 support for GLX_SWAP_EXCHANGE_OML and GLX_SWAP_COPY_OML.

The back-to-fake-front flips will save a full buffer copy in the case of a
fake front being enabled and GLX_SWAP_UNDEFINED_OML.

Support for EGL_BUFFER_PRESERVED and GLX_SWAP_X_OML are mostly useful for
things like glretrace if traces are capured with applications relying on a
specific swapbuffer behavior.

The EGL_BUFFER_PRESERVED path previously made sure the present was done as
a copy, but there was nothing making sure that after the present,
the same back buffer was chosen.
This has now been changed so that if the previous back buffer is
idle, we reuse it. Otherwise we grab a new and copy the contents and
buffer age from the previous back buffer. Server side flips are allowed.

GLX_SWAP_COPY_OML will behave like EGL_BUFFER_PRESERVED.

GLX_SWAP_EXCHANGE_OML will behave similarly, except that we try to reuse the
previous fake front as the new back buffer if it's idle. If not, we grab
a new back buffer and copy the contents and buffer age from the old fake front.

Signed-off-by: Thomas Hellstrom <thellstrom at vmware.com>
---
 src/loader/loader_dri3_helper.c | 126 ++++++++++++++++++++++++++++++++--------
 src/loader/loader_dri3_helper.h |   4 ++
 2 files changed, 105 insertions(+), 25 deletions(-)

diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c
index 9d24130..3323706 100644
--- a/src/loader/loader_dri3_helper.c
+++ b/src/loader/loader_dri3_helper.c
@@ -40,6 +40,9 @@
 #define DRI_CONF_VBLANK_DEF_INTERVAL_1 2
 #define DRI_CONF_VBLANK_ALWAYS_SYNC 3
 
+static void
+dri3_flush_present_events(struct loader_dri3_drawable *draw);
+
 static inline void
 dri3_fence_reset(xcb_connection_t *c, struct loader_dri3_buffer *buffer)
 {
@@ -148,7 +151,9 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
    draw->have_back = 0;
    draw->have_fake_front = 0;
    draw->first_init = true;
-
+   draw->cur_blit_source = -1;
+   draw->have_image_blit = draw->ext->image->base.version >= 9 &&
+      draw->ext->image->blitImage != NULL;
    if (draw->ext->config)
       draw->ext->config->configQueryi(draw->dri_screen,
                                       "vblank_mode", &vblank_mode);
@@ -190,6 +195,13 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
    draw->vtable->set_drawable_size(draw, draw->width, draw->height);
    free(reply);
 
+   draw->swap_method = __DRI_ATTRIB_SWAP_UNDEFINED;
+   if (draw->ext->core->base.version >= 2) {
+      (void )draw->ext->core->getConfigAttrib(dri_config,
+                                              __DRI_ATTRIB_SWAP_METHOD,
+                                              &draw->swap_method);
+   }
+
    /*
     * Make sure server has the same swap interval we do for the new
     * drawable.
@@ -363,11 +375,24 @@ static int
 dri3_find_back(struct loader_dri3_drawable *draw)
 {
    int b;
+   int num_to_consider = draw->num_back;
    xcb_generic_event_t *ev;
    xcb_present_generic_event_t *ge;
 
+   /* Increase the likelyhood of reusing current buffer */
+   dri3_flush_present_events(draw);
+
+   /* Check whether we need to reuse the current back buffer as new back.
+    * In that case, wait until it's not busy anymore,
+    * only considering that particular buffer.
+    */
+   if (!draw->have_image_blit && draw->cur_blit_source != -1) {
+      num_to_consider = 1;
+      draw->cur_blit_source = -1;
+   }
+
    for (;;) {
-      for (b = 0; b < draw->num_back; b++) {
+      for (b = 0; b < num_to_consider; b++) {
          int id = LOADER_DRI3_BACK_ID((b + draw->cur_back) % draw->num_back);
          struct loader_dri3_buffer *buffer = draw->buffers[id];
 
@@ -649,14 +674,27 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
                                   back->height,
                                   0, 0, back->width,
                                   back->height, __BLIT_FLAG_FLUSH);
-      /* Update the fake front */
-      if (draw->have_fake_front)
-         draw->ext->image->blitImage(dri_context,
-                                     draw->buffers[LOADER_DRI3_FRONT_ID]->image,
-                                     back->image,
-                                     0, 0, draw->width, draw->height,
-                                     0, 0, draw->width, draw->height,
-                                     __BLIT_FLAG_FLUSH);
+   }
+
+   /* If we need to preload the new back buffer, remember the source.
+    * The force_copy parameter is used by EGL to attempt to preserve
+    * the back buffer across a call to this function.
+    */
+   if (draw->swap_method != __DRI_ATTRIB_SWAP_UNDEFINED || force_copy)
+      draw->cur_blit_source = LOADER_DRI3_BACK_ID(draw->cur_back);
+
+   /* Flip the back and fake front. Even though the server knows about these
+    * buffers, it has no notion of back and fake front.
+    */
+   if (back && draw->have_fake_front) {
+      struct loader_dri3_buffer *tmp;
+
+      tmp = dri3_fake_front_buffer(draw);
+      draw->buffers[LOADER_DRI3_FRONT_ID] = back;
+      draw->buffers[LOADER_DRI3_BACK_ID(draw->cur_back)] = tmp;
+
+      if (draw->swap_method == __DRI_ATTRIB_SWAP_COPY)
+         draw->cur_blit_source = LOADER_DRI3_FRONT_ID;
    }
 
    dri3_flush_present_events(draw);
@@ -697,7 +735,12 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
        */
       if (draw->swap_interval == 0)
           options |= XCB_PRESENT_OPTION_ASYNC;
-      if (force_copy)
+
+      /* If we need to populate the new back, but need to reuse the back
+       * buffer slot due to lack of local blit capabilities, make sure
+       * the server doesn't flip and we deadlock.
+       */
+      if (!draw->have_image_blit && draw->cur_blit_source != -1)
           options |= XCB_PRESENT_OPTION_COPY;
 
       back->busy = 1;
@@ -719,21 +762,26 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
                          remainder, 0, NULL);
       ret = (int64_t) draw->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
+      /* Schedule a server-side back-preserving blit if necessary.
+       * This basically happens iff all conditions below are satisfied:
+       * a) We have a fake front,
+       * b) We need to preserve the back buffer,
+       * c) We don't have local blit capabilities.
        */
-      if (draw->have_fake_front && !draw->is_different_gpu) {
-         dri3_fence_reset(draw->conn, draw->buffers[LOADER_DRI3_FRONT_ID]);
-         dri3_copy_area(draw->conn,
-                        back->pixmap,
-                        draw->buffers[LOADER_DRI3_FRONT_ID]->pixmap,
+      if (!draw->have_image_blit && draw->cur_blit_source != -1 &&
+          draw->cur_blit_source != LOADER_DRI3_BACK_ID(draw->cur_back)) {
+         struct loader_dri3_buffer *new_back = dri3_back_buffer(draw);
+         struct loader_dri3_buffer *src = draw->buffers[draw->cur_blit_source];
+
+         dri3_fence_reset(draw->conn, new_back);
+         dri3_copy_area(draw->conn, src->pixmap,
+                        new_back->pixmap,
                         dri3_drawable_gc(draw),
-                        0, 0, 0, 0,
-                        draw->width, draw->height);
-         dri3_fence_trigger(draw->conn, draw->buffers[LOADER_DRI3_FRONT_ID]);
+                        0, 0, 0, 0, draw->width, draw->height);
+         dri3_fence_trigger(draw->conn, new_back);
+         new_back->last_swap = src->last_swap;
       }
+
       xcb_flush(draw->conn);
       if (draw->stamp)
          ++(*draw->stamp);
@@ -1284,6 +1332,32 @@ dri3_get_buffer(__DRIdrawable *driDrawable,
    }
    dri3_fence_await(draw->conn, buffer);
 
+   /*
+    * Do we need to preserve the content of a previous buffer?
+    *
+    * Note that this blit is needed only to avoid a wait for a buffer that
+    * is currently in the flip chain or being scanned out from. That's really
+    * a tradeoff. If we're ok with the wait we can reduce the number of back
+    * buffers to 1 for SWAP_EXCHANGE, and 1 for SWAP_COPY,
+    * but in the latter case we must disallow page-flipping.
+    */
+   if (buffer_type == loader_dri3_buffer_back &&
+       draw->vtable->in_current_context(draw) &&
+       draw->cur_blit_source != -1 &&
+       draw->buffers[draw->cur_blit_source] &&
+       buffer != draw->buffers[draw->cur_blit_source]) {
+      struct loader_dri3_buffer *source = draw->buffers[draw->cur_blit_source];
+
+      /* Avoid flushing here. Will propably do good for tiling hardware. */
+      draw->ext->image->blitImage(dri_context,
+                                  buffer->image,
+                                  source->image,
+                                  0, 0, draw->width, draw->height,
+                                  0, 0, draw->width, draw->height, 0);
+      buffer->last_swap = source->last_swap;
+      draw->cur_blit_source = -1;
+   }
+
    /* Return the requested buffer */
    return buffer;
 }
@@ -1349,8 +1423,10 @@ loader_dri3_get_buffers(__DRIdrawable *driDrawable,
    if (!dri3_update_drawable(driDrawable, draw))
       return false;
 
-   /* pixmaps always have front buffers */
-   if (draw->is_pixmap)
+   /* pixmaps always have front buffers.
+    * Exchange swaps also mandates front buffers.
+    */
+   if (draw->is_pixmap || draw->swap_method == __DRI_ATTRIB_SWAP_EXCHANGE)
       buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
 
    if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
diff --git a/src/loader/loader_dri3_helper.h b/src/loader/loader_dri3_helper.h
index af42425..b886267 100644
--- a/src/loader/loader_dri3_helper.h
+++ b/src/loader/loader_dri3_helper.h
@@ -146,6 +146,7 @@ struct loader_dri3_drawable {
    struct loader_dri3_buffer *buffers[LOADER_DRI3_NUM_BUFFERS];
    int cur_back;
    int num_back;
+   int cur_blit_source;
 
    uint32_t *stamp;
 
@@ -158,6 +159,9 @@ struct loader_dri3_drawable {
 
    struct loader_dri3_extensions *ext;
    const struct loader_dri3_vtable *vtable;
+
+   unsigned int swap_method;
+   bool have_image_blit;
 };
 
 void
-- 
2.7.4



More information about the mesa-dev mailing list