EGL uses the force_copy parameter to loader_dri3_swap_buffers_msc() to indicate
that it wants to preserve back buffer contents across a buffer swap.

While the loader then turns off server-side page-flipping there's nothing to
guarantee that a new backbuffer isn't chosen when EGL starts to render again,
and that buffer's content is of course undefined.

So rework the functionality:
If the client supports local blits, allow server-side page flipping and when
a new back is grabbed, if needed, blit the old back's content to the new back.
If the client doesn't support local blits, disallow server-side page-flipping
to avoid a client deadlock and then, when grabbing a new back buffer, sleep
until the old back is idle, which may take a substantial time depending on
swap interval.

Signed-off-by: Thomas Hellstrom <thellst...@vmware.com>
---
 src/loader/loader_dri3_helper.c | 55 ++++++++++++++++++++++++++++++++++++++---
 src/loader/loader_dri3_helper.h |  1 +
 2 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c
index 6f84a43..812adcc 100644
--- a/src/loader/loader_dri3_helper.c
+++ b/src/loader/loader_dri3_helper.c
@@ -258,6 +258,7 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
 
    draw->have_image_blit = draw->ext->image->base.version >= 9 &&
       draw->ext->image->blitImage != NULL;
+   draw->cur_blit_source = -1;
 
    if (draw->ext->config)
       draw->ext->config->configQueryi(draw->dri_screen,
@@ -475,12 +476,21 @@ dri3_find_back(struct loader_dri3_drawable *draw)
    int b;
    xcb_generic_event_t *ev;
    xcb_present_generic_event_t *ge;
+   int num_to_consider = draw->num_back;
 
    /* 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.
+    */
+   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];
 
@@ -753,6 +763,13 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable 
*draw,
                                        0, 0, __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 (force_copy)
+      draw->cur_blit_source = LOADER_DRI3_BACK_ID(draw->cur_back);
+
    dri3_flush_present_events(draw);
 
    if (back && !draw->is_pixmap) {
@@ -791,8 +808,13 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable 
*draw,
        */
       if (draw->swap_interval == 0)
           options |= XCB_PRESENT_OPTION_ASYNC;
-      if (force_copy)
-          options |= XCB_PRESENT_OPTION_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;
       back->last_swap = draw->send_sbc;
@@ -1365,6 +1387,31 @@ 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->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. */
+      (void) loader_dri3_blit_image(draw,
+                                    buffer->image,
+                                    source->image,
+                                    0, 0, draw->width, draw->height,
+                                    0, 0, 0);
+      buffer->last_swap = source->last_swap;
+      draw->cur_blit_source = -1;
+   }
    /* Return the requested buffer */
    return buffer;
 }
@@ -1431,7 +1478,7 @@ loader_dri3_get_buffers(__DRIdrawable *driDrawable,
       return false;
 
    /* pixmaps always have front buffers */
-//   if (draw->is_pixmap)
+   if (draw->is_pixmap)
       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 be41da9..82c5363 100644
--- a/src/loader/loader_dri3_helper.h
+++ b/src/loader/loader_dri3_helper.h
@@ -145,6 +145,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;
 
-- 
2.7.4

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to