xwayland: Replace window pixmap as needed for drawing operation

We must not modify the contents of a client pixmap.

If there's an available window buffer, we re-use that for the window
pixmap. Otherwise we just allocate a new one.

This also avoids Present client hangs due to xwl_present_buffer_release
not getting called for the buffer release event.

v2:
* Use xwl_pixmap_get_buffer_release_cb instead of keeping track of the
  flip pixmap in xwl_window.
* Dispose of xwl_window_buffer in xwl_window_swap_pixmap called from
  damage_report.
v3:
* Use xwl_window->surface_pixmap in damage_report.
v4:
* Don't re-use client pixmaps as window buffers.
* Clear xwl_window_buffer->pixmap before calling
  xwl_window_buffer_maybe_dispose in xwl_window_swap_pixmap, to prevent
  it from clearing the buffer release callback.
v5:
* Keep using xwl_window_buffers_get_pixmap in xwl_window_attach_buffer.
* Always keep a reference to the old window pixmap in _swap_pixmap,
  drop it in damage_report.

Fixes: 6779ec5bf6 ("xwayland: Use window pixmap as a window buffer")
Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1633
Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1644
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1314>
This commit is contained in:
Michel Dänzer 2024-02-22 12:21:29 +01:00 committed by Michel Dänzer
parent 0e29cccf36
commit 6b290fa5d9
4 changed files with 29 additions and 7 deletions

View File

@ -45,4 +45,10 @@ void xwl_pixmap_del_buffer_release_cb(PixmapPtr pixmap);
void xwl_pixmap_buffer_release_cb(void *data, struct wl_buffer *wl_buffer);
Bool xwl_pixmap_init(void);
static inline Bool
xwl_is_client_pixmap(PixmapPtr pixmap)
{
return clients[CLIENT_ID(pixmap->drawable.id)] != serverClient;
}
#endif /* XWAYLAND_PIXMAP_H */

View File

@ -78,8 +78,7 @@ xwl_window_buffer_new(struct xwl_window *xwl_window)
xwl_window_buffer->pixmap = NullPixmap;
xwl_window_buffer->refcnt = 1;
xorg_list_append(&xwl_window_buffer->link_buffer,
&xwl_window->window_buffers_available);
xorg_list_init(&xwl_window_buffer->link_buffer);
return xwl_window_buffer;
}
@ -331,7 +330,7 @@ xwl_window_realloc_pixmap(struct xwl_window *xwl_window)
screen->DestroyPixmap(window_pixmap);
}
static PixmapPtr
PixmapPtr
xwl_window_swap_pixmap(struct xwl_window *xwl_window)
{
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
@ -360,17 +359,27 @@ xwl_window_swap_pixmap(struct xwl_window *xwl_window)
}
RegionEmpty(xwl_window_buffer->damage_region);
xorg_list_del(&xwl_window_buffer->link_buffer);
xwl_window_set_pixmap(xwl_window->window, xwl_window_buffer->pixmap);
/* Can't re-use client pixmap as a window buffer */
if (xwl_is_client_pixmap(window_pixmap)) {
xwl_window_buffer->pixmap = NULL;
xwl_window_buffer_maybe_dispose(xwl_window_buffer);
return window_pixmap;
}
} else {
/* Can't re-use client pixmap as a window buffer */
if (!xwl_is_client_pixmap(window_pixmap))
xwl_window_buffer = xwl_window_buffer_new(xwl_window);
window_pixmap->refcnt++;
xwl_window_realloc_pixmap(xwl_window);
xwl_window_buffer = xwl_window_buffer_new(xwl_window);
if (!xwl_window_buffer)
return window_pixmap;
}
xorg_list_del(&xwl_window_buffer->link_buffer);
xwl_window_set_pixmap(xwl_window->window, xwl_window_buffer->pixmap);
xwl_window_buffer->pixmap = window_pixmap;
/* Hold a reference on the buffer until it's released by the compositor */

View File

@ -31,10 +31,13 @@
#include "xwayland-types.h"
struct xwl_window_buffer;
void xwl_window_buffer_add_damage_region(struct xwl_window *xwl_window);
void xwl_window_buffers_init(struct xwl_window *xwl_window);
void xwl_window_buffers_dispose(struct xwl_window *xwl_window);
void xwl_window_realloc_pixmap(struct xwl_window *xwl_window);
PixmapPtr xwl_window_swap_pixmap(struct xwl_window *xwl_window);
PixmapPtr xwl_window_buffers_get_pixmap(struct xwl_window *xwl_window);
#endif /* XWAYLAND_WINDOW_BUFFERS_H */

View File

@ -188,6 +188,7 @@ damage_report(DamagePtr pDamage, RegionPtr pRegion, void *data)
WindowPtr window = data;
struct xwl_window *xwl_window = xwl_window_get(window);
struct xwl_screen *xwl_screen;
PixmapPtr window_pixmap;
if (!xwl_window)
return;
@ -207,6 +208,10 @@ damage_report(DamagePtr pDamage, RegionPtr pRegion, void *data)
if (xorg_list_is_empty(&xwl_window->link_damage))
xorg_list_add(&xwl_window->link_damage, &xwl_screen->damage_window_list);
window_pixmap = xwl_screen->screen->GetWindowPixmap(xwl_window->window);
if (xwl_is_client_pixmap(window_pixmap))
xwl_screen->screen->DestroyPixmap(xwl_window_swap_pixmap(xwl_window));
}
static void
@ -227,7 +232,6 @@ register_damage(WindowPtr window)
}
DamageRegister(&window->drawable, damage);
DamageSetReportAfterOp(damage, TRUE);
dixSetPrivate(&window->devPrivates, &xwl_damage_private_key, damage);