loader/dri3: avoid reusing the same back buffer with DRI_PRIME

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 0cc4c7e33e).

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>
This commit is contained in:
Pierre-Eric Pelloux-Prayer 2021-09-08 20:08:57 +02:00 committed by Marge Bot
parent e67083caf4
commit 09dc0fb2ee
1 changed files with 17 additions and 4 deletions

View 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;