loader/dri3: avoid reusing the same back buffer with DRI_PRIME
authorPierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Wed, 8 Sep 2021 18:08:57 +0000 (20:08 +0200)
committerMarge Bot <eric+marge@anholt.net>
Wed, 29 Sep 2021 08:14:57 +0000 (08:14 +0000)
For DRI_PRIME setup where the dGPU -> iGPU copy can happen asynchronously,
we need to ensure that we're not continuously reusing the same back buffer.

The existing code relies on XCB_PRESENT_EVENT_IDLE_NOTIFY to decide if
a buffer is busy or idle. If this event is received before the hardware
is done using the buffer, then it will reuse the same buffer and introduce
a dependency between the copy and the next frame.

This commit mitigates this by trying to allocate a different back buffer
when called from dri3_get_buffer (not from dri3_find_back_alloc, because it
seems that it expects dri3_get_buffer - see 0cc4c7e33ed).

An alternative would be to query the busy-ness using is_resource_busy
but this complicates the code to achieve the same result.

One affected app is Unigine Superposition, and this change improves the
score by 0% - 5% depending on the settings.

This behavior is enabled if PIPE_CAP_PREFER_BACK_BUFFER_REUSE is 0.

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Acked-by: Michel Dänzer <mdaenzer@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12788>

src/loader/loader_dri3_helper.c

index 16cb1fa..e808bcf 100644 (file)
@@ -679,7 +679,7 @@ loader_dri3_wait_for_sbc(struct loader_dri3_drawable *draw,
  * wait for a present idle notify event from the X server
  */
 static int
-dri3_find_back(struct loader_dri3_drawable *draw)
+dri3_find_back(struct loader_dri3_drawable *draw, bool prefer_a_different)
 {
    int b;
    int num_to_consider;
@@ -701,12 +701,23 @@ dri3_find_back(struct loader_dri3_drawable *draw)
       max_num = draw->max_num_back;
    }
 
+   /* In a DRI_PRIME situation, if prefer_a_different is true, we first try
+    * to find an idle buffer that is not the last used one.
+    * This is useful if we receive a XCB_PRESENT_EVENT_IDLE_NOTIFY event
+    * for a pixmap but it's not actually idle (eg: the DRI_PRIME blit is
+    * still in progress).
+    * Unigine Superposition hits this and this allows to use 2 back buffers
+    * instead of reusing the same one all the time, causing the next frame
+    * to wait for the copy to finish.
+    */
+   int current_back_id = draw->cur_back;
    for (;;) {
       for (b = 0; b < num_to_consider; b++) {
          int id = LOADER_DRI3_BACK_ID((b + draw->cur_back) % draw->cur_num_back);
          struct loader_dri3_buffer *buffer = draw->buffers[id];
 
-         if (!buffer || !buffer->busy) {
+         if (!buffer || (!buffer->busy &&
+                         (!prefer_a_different || id != current_back_id))) {
             draw->cur_back = id;
             mtx_unlock(&draw->mtx);
             return id;
@@ -715,6 +726,8 @@ dri3_find_back(struct loader_dri3_drawable *draw)
 
       if (num_to_consider < max_num) {
          num_to_consider = ++draw->cur_num_back;
+      } else if (prefer_a_different) {
+         prefer_a_different = false;
       } else if (!dri3_wait_for_event_locked(draw, NULL)) {
          mtx_unlock(&draw->mtx);
          return -1;
@@ -1921,7 +1934,7 @@ dri3_get_buffer(__DRIdrawable *driDrawable,
    if (buffer_type == loader_dri3_buffer_back) {
       draw->back_format = format;
 
-      buf_id = dri3_find_back(draw);
+      buf_id = dri3_find_back(draw, !draw->prefer_back_buffer_reuse);
 
       if (buf_id < 0)
          return NULL;
@@ -2246,7 +2259,7 @@ dri3_find_back_alloc(struct loader_dri3_drawable *draw)
    struct loader_dri3_buffer *back;
    int id;
 
-   id = dri3_find_back(draw);
+   id = dri3_find_back(draw, false);
    if (id < 0)
       return NULL;