loader/dri3: Add DRI performance option to wait for next buffer on swap

For simple clients using the swap chain contention back pressure to regulate
their drawing and that don't query buffer age introduce a new DRI option with
which set to true (the default is false) we block the client until a new buffer
is available. This way we stall the client's execution until a new buffer is
available and the redrawing of the client starts only at this point and not
before.

The motivation for that is to reduce latency for clients that regulate their
drawing by swapchain contention back pressure. These clients draw whenever
possible and their drawing is implicitly stopping whenever we block. When we
block at the end of the swap and return only when a new buffer is available
the client can draw and we directly present. Otherwise the client would draw,
we block on the buffer becoming available, and only then show what the client
had drawn, usually one frame later.

Co-authored-by: Michel Dänzer <michel@daenzer.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14684>
This commit is contained in:
Roman Gilg 2022-01-23 14:04:05 +01:00 committed by Marge Bot
parent d54464279c
commit 43d93c32c9
4 changed files with 42 additions and 0 deletions

View file

@ -58,6 +58,7 @@ driOptionDescription __dri2ConfigOptions[] = {
DRI_CONF_SECTION_PERFORMANCE DRI_CONF_SECTION_PERFORMANCE
DRI_CONF_VBLANK_MODE(DRI_CONF_VBLANK_DEF_INTERVAL_1) DRI_CONF_VBLANK_MODE(DRI_CONF_VBLANK_DEF_INTERVAL_1)
DRI_CONF_BLOCK_ON_DEPLETED_BUFFERS(false)
DRI_CONF_SECTION_END DRI_CONF_SECTION_END
}; };

View file

@ -402,12 +402,14 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
draw->is_different_gpu = is_different_gpu; draw->is_different_gpu = is_different_gpu;
draw->multiplanes_available = multiplanes_available; draw->multiplanes_available = multiplanes_available;
draw->prefer_back_buffer_reuse = prefer_back_buffer_reuse; draw->prefer_back_buffer_reuse = prefer_back_buffer_reuse;
draw->queries_buffer_age = false;
draw->have_back = 0; draw->have_back = 0;
draw->have_fake_front = 0; draw->have_fake_front = 0;
draw->first_init = true; draw->first_init = true;
draw->adaptive_sync = false; draw->adaptive_sync = false;
draw->adaptive_sync_active = false; draw->adaptive_sync_active = false;
draw->block_on_depleted_buffers = false;
draw->cur_blit_source = -1; draw->cur_blit_source = -1;
draw->back_format = __DRI_IMAGE_FORMAT_NONE; draw->back_format = __DRI_IMAGE_FORMAT_NONE;
@ -416,12 +418,19 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
if (draw->ext->config) { if (draw->ext->config) {
unsigned char adaptive_sync = 0; unsigned char adaptive_sync = 0;
unsigned char block_on_depleted_buffers = 0;
draw->ext->config->configQueryb(draw->dri_screen, draw->ext->config->configQueryb(draw->dri_screen,
"adaptive_sync", "adaptive_sync",
&adaptive_sync); &adaptive_sync);
draw->adaptive_sync = adaptive_sync; draw->adaptive_sync = adaptive_sync;
draw->ext->config->configQueryb(draw->dri_screen,
"block_on_depleted_buffers",
&block_on_depleted_buffers);
draw->block_on_depleted_buffers = block_on_depleted_buffers;
} }
if (!draw->adaptive_sync) if (!draw->adaptive_sync)
@ -975,6 +984,7 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
{ {
struct loader_dri3_buffer *back; struct loader_dri3_buffer *back;
int64_t ret = 0; int64_t ret = 0;
bool wait_for_next_buffer = false;
/* GLX spec: /* GLX spec:
* void glXSwapBuffers(Display *dpy, GLXDrawable draw); * void glXSwapBuffers(Display *dpy, GLXDrawable draw);
@ -1198,10 +1208,34 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
if (draw->stamp) if (draw->stamp)
++(*draw->stamp); ++(*draw->stamp);
/* Waiting on a buffer is only sensible if all buffers are in use and the
* client doesn't use the buffer age extension. In this case a client is
* relying on it receiving back control immediately.
*
* As waiting on a buffer can at worst make us miss a frame the option has
* to be enabled explicitly with the block_on_depleted_buffers DRI option.
*/
wait_for_next_buffer = draw->cur_num_back == draw->max_num_back &&
!draw->queries_buffer_age && draw->block_on_depleted_buffers;
mtx_unlock(&draw->mtx); mtx_unlock(&draw->mtx);
draw->ext->flush->invalidate(draw->dri_drawable); draw->ext->flush->invalidate(draw->dri_drawable);
/* Clients that use up all available buffers usually regulate their drawing
* through swapchain contention backpressure. In such a scenario the client
* draws whenever control returns to it. Its event loop is slowed down only
* by us waiting on buffers becoming available again.
*
* By waiting here on a new buffer and only then returning back to the client
* we ensure the client begins drawing only when the next buffer is available
* and not draw first and then wait a refresh cycle on the next available
* buffer to show it. This way we can reduce the latency between what is
* being drawn by the client and what is shown on the screen by one frame.
*/
if (wait_for_next_buffer)
dri3_find_back(draw, draw->prefer_back_buffer_reuse);
return ret; return ret;
} }
@ -1212,6 +1246,7 @@ loader_dri3_query_buffer_age(struct loader_dri3_drawable *draw)
int ret = 0; int ret = 0;
mtx_lock(&draw->mtx); mtx_lock(&draw->mtx);
draw->queries_buffer_age = true;
if (back && back->last_swap != 0) if (back && back->last_swap != 0)
ret = draw->send_sbc - back->last_swap + 1; ret = draw->send_sbc - back->last_swap + 1;
mtx_unlock(&draw->mtx); mtx_unlock(&draw->mtx);

View file

@ -173,6 +173,8 @@ struct loader_dri3_drawable {
bool first_init; bool first_init;
bool adaptive_sync; bool adaptive_sync;
bool adaptive_sync_active; bool adaptive_sync_active;
bool block_on_depleted_buffers;
bool queries_buffer_age;
int swap_interval; int swap_interval;
struct loader_dri3_extensions *ext; struct loader_dri3_extensions *ext;

View file

@ -360,6 +360,10 @@
DRI_CONF_OPT_B(adaptive_sync,def, \ DRI_CONF_OPT_B(adaptive_sync,def, \
"Adapt the monitor sync to the application performance (when possible)") "Adapt the monitor sync to the application performance (when possible)")
#define DRI_CONF_BLOCK_ON_DEPLETED_BUFFERS(def) \
DRI_CONF_OPT_B(block_on_depleted_buffers, def, \
"Block clients using buffer backpressure until new buffer is available to reduce latency")
#define DRI_CONF_VK_WSI_FORCE_BGRA8_UNORM_FIRST(def) \ #define DRI_CONF_VK_WSI_FORCE_BGRA8_UNORM_FIRST(def) \
DRI_CONF_OPT_B(vk_wsi_force_bgra8_unorm_first, def, \ DRI_CONF_OPT_B(vk_wsi_force_bgra8_unorm_first, def, \
"Force vkGetPhysicalDeviceSurfaceFormatsKHR to return VK_FORMAT_B8G8R8A8_UNORM as the first format") "Force vkGetPhysicalDeviceSurfaceFormatsKHR to return VK_FORMAT_B8G8R8A8_UNORM as the first format")