egl/wayland: break double/tripple buffering feedback loops

Currently we dispose any unneeded color buffers immediately if we detect that
there are more unlocked buffers than we need. This can lead to feedback loops
between the compositor and the application causing rapid toggling between
double and tripple buffering.

Scenario: 2 buffers already queued to the compositor, egl/wayland allocates a
new back buffer to avoid throttling, slowing down the frame. This allows the
compositor to catch up and unlock both buffers. EGL detects that there are
more buffers than currently needed, freeing the buffer, restarting the loop
shortly after.

To avoid wasting CPU time on rapidly freeing and reallocating color buffers
break those feedback loops by letting the unneeded buffers sit around for a
short while before disposing them.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Reviewed-by: Simon Ser <contact@emersion.fr>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14451>
This commit is contained in:
Lucas Stach 2022-01-07 12:53:21 +01:00 committed by Marge Bot
parent d77bfc117c
commit 22d796feb8
1 changed files with 16 additions and 4 deletions

View File

@ -1096,6 +1096,12 @@ back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
buffer->flags = 0;
}
/* Value chosen empirically as a compromise between avoiding frequent
* reallocations and extended time of increased memory consumption due to
* unused buffers being kept.
*/
#define BUFFER_TRIM_AGE_HYSTERESIS 20
static int
update_buffers(struct dri2_egl_surface *dri2_surf)
{
@ -1125,10 +1131,13 @@ update_buffers(struct dri2_egl_surface *dri2_surf)
/* If we have an extra unlocked buffer at this point, we had to do triple
* buffering for a while, but now can go back to just double buffering.
* That means we can free any unlocked buffer now. */
* That means we can free any unlocked buffer now. To avoid toggling between
* going back to double buffering and needing to allocate another buffer too
* fast we let the unneeded buffer sit around for a short while. */
for (int i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
if (!dri2_surf->color_buffers[i].locked &&
dri2_surf->color_buffers[i].wl_buffer) {
dri2_surf->color_buffers[i].wl_buffer &&
dri2_surf->color_buffers[i].age > BUFFER_TRIM_AGE_HYSTERESIS) {
wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
if (dri2_dpy->is_different_gpu)
@ -2321,10 +2330,13 @@ swrast_update_buffers(struct dri2_egl_surface *dri2_surf)
/* If we have an extra unlocked buffer at this point, we had to do triple
* buffering for a while, but now can go back to just double buffering.
* That means we can free any unlocked buffer now. */
* That means we can free any unlocked buffer now. To avoid toggling between
* going back to double buffering and needing to allocate another buffer too
* fast we let the unneeded buffer sit around for a short while. */
for (int i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
if (!dri2_surf->color_buffers[i].locked &&
dri2_surf->color_buffers[i].wl_buffer) {
dri2_surf->color_buffers[i].wl_buffer &&
dri2_surf->color_buffers[i].age > BUFFER_TRIM_AGE_HYSTERESIS) {
wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
munmap(dri2_surf->color_buffers[i].data,
dri2_surf->color_buffers[i].data_size);