winewayland.drv: Use a buffer queue for window_surface.

Create a buffer queue to hold the SHM buffers for each window_surface,
to be able to reuse released buffers instead of constantly creating new
ones.
This commit is contained in:
Alexandros Frantzis 2023-05-18 14:13:27 +03:00 committed by Alexandre Julliard
parent 6bf3b5539a
commit 4371a26041
3 changed files with 192 additions and 17 deletions

View file

@ -221,6 +221,9 @@ void wayland_surface_clear_role(struct wayland_surface *surface)
* wayland_surface_attach_shm
*
* Attaches a SHM buffer to a wayland surface.
*
* The buffer is marked as unavailable until committed and subsequently
* released by the compositor.
*/
void wayland_surface_attach_shm(struct wayland_surface *surface,
struct wayland_shm_buffer *shm_buffer)
@ -228,19 +231,34 @@ void wayland_surface_attach_shm(struct wayland_surface *surface,
TRACE("surface=%p shm_buffer=%p (%dx%d)\n",
surface, shm_buffer, shm_buffer->width, shm_buffer->height);
shm_buffer->busy = TRUE;
wayland_shm_buffer_ref(shm_buffer);
wl_surface_attach(surface->wl_surface, shm_buffer->wl_buffer, 0, 0);
wl_surface_damage_buffer(surface->wl_surface, 0, 0,
shm_buffer->width, shm_buffer->height);
}
/**********************************************************************
* wayland_shm_buffer_destroy
* wayland_shm_buffer_ref
*
* Destroys a SHM buffer.
* Increases the reference count of a SHM buffer.
*/
void wayland_shm_buffer_destroy(struct wayland_shm_buffer *shm_buffer)
void wayland_shm_buffer_ref(struct wayland_shm_buffer *shm_buffer)
{
TRACE("%p map=%p\n", shm_buffer, shm_buffer->map_data);
InterlockedIncrement(&shm_buffer->ref);
}
/**********************************************************************
* wayland_shm_buffer_unref
*
* Decreases the reference count of a SHM buffer (and may destroy it).
*/
void wayland_shm_buffer_unref(struct wayland_shm_buffer *shm_buffer)
{
if (InterlockedDecrement(&shm_buffer->ref) > 0) return;
TRACE("destroying %p map=%p\n", shm_buffer, shm_buffer->map_data);
if (shm_buffer->wl_buffer)
wl_buffer_destroy(shm_buffer->wl_buffer);
@ -284,6 +302,7 @@ struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height,
TRACE("%p %dx%d format=%d size=%d\n", shm_buffer, width, height, format, size);
shm_buffer->ref = 1;
shm_buffer->width = width;
shm_buffer->height = height;
shm_buffer->map_size = size;
@ -340,6 +359,6 @@ struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height,
err:
if (fd >= 0) close(fd);
if (handle) NtClose(handle);
if (shm_buffer) wayland_shm_buffer_destroy(shm_buffer);
if (shm_buffer) wayland_shm_buffer_unref(shm_buffer);
return NULL;
}

View file

@ -107,10 +107,13 @@ struct wayland_surface
struct wayland_shm_buffer
{
struct wl_list link;
struct wl_buffer *wl_buffer;
int width, height;
void *map_data;
size_t map_size;
BOOL busy;
LONG ref;
};
/**********************************************************************
@ -145,7 +148,8 @@ void wayland_surface_attach_shm(struct wayland_surface *surface,
struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height,
enum wl_shm_format format) DECLSPEC_HIDDEN;
void wayland_shm_buffer_destroy(struct wayland_shm_buffer *shm_buffer) DECLSPEC_HIDDEN;
void wayland_shm_buffer_ref(struct wayland_shm_buffer *shm_buffer) DECLSPEC_HIDDEN;
void wayland_shm_buffer_unref(struct wayland_shm_buffer *shm_buffer) DECLSPEC_HIDDEN;
/**********************************************************************
* Wayland window surface

View file

@ -32,11 +32,20 @@
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
struct wayland_buffer_queue
{
struct wl_event_queue *wl_event_queue;
struct wl_list buffer_list;
int width;
int height;
};
struct wayland_window_surface
{
struct window_surface header;
HWND hwnd;
struct wayland_surface *wayland_surface;
struct wayland_buffer_queue *wayland_buffer_queue;
RECT bounds;
void *bits;
pthread_mutex_t mutex;
@ -59,11 +68,143 @@ static void buffer_release(void *data, struct wl_buffer *buffer)
{
struct wayland_shm_buffer *shm_buffer = data;
TRACE("shm_buffer=%p\n", shm_buffer);
wayland_shm_buffer_destroy(shm_buffer);
shm_buffer->busy = FALSE;
wayland_shm_buffer_unref(shm_buffer);
}
static const struct wl_buffer_listener buffer_listener = { buffer_release };
/**********************************************************************
* wayland_buffer_queue_destroy
*
* Destroys a buffer queue and any contained buffers.
*/
static void wayland_buffer_queue_destroy(struct wayland_buffer_queue *queue)
{
struct wayland_shm_buffer *shm_buffer, *next;
wl_list_for_each_safe(shm_buffer, next, &queue->buffer_list, link)
{
wl_list_remove(&shm_buffer->link);
wl_list_init(&shm_buffer->link);
/* Since this buffer may still be busy, attach it to the per-process
* wl_event_queue to handle any future buffer release events. */
wl_proxy_set_queue((struct wl_proxy *)shm_buffer->wl_buffer,
process_wayland.wl_event_queue);
wayland_shm_buffer_unref(shm_buffer);
}
if (queue->wl_event_queue)
{
/* Dispatch the event queue before destruction to process any
* pending buffer release events. This is required after changing
* the buffer proxy event queue in the previous step, to avoid
* missing any events. */
wl_display_dispatch_queue_pending(process_wayland.wl_display,
queue->wl_event_queue);
wl_event_queue_destroy(queue->wl_event_queue);
}
free(queue);
}
/**********************************************************************
* wayland_buffer_queue_create
*
* Creates a buffer queue containing buffers with the specified width and height.
*/
static struct wayland_buffer_queue *wayland_buffer_queue_create(int width, int height)
{
struct wayland_buffer_queue *queue;
queue = calloc(1, sizeof(*queue));
if (!queue) goto err;
queue->wl_event_queue = wl_display_create_queue(process_wayland.wl_display);
if (!queue->wl_event_queue) goto err;
queue->width = width;
queue->height = height;
wl_list_init(&queue->buffer_list);
return queue;
err:
if (queue) wayland_buffer_queue_destroy(queue);
return NULL;
}
/**********************************************************************
* wayland_buffer_queue_get_free_buffer
*
* Gets a free buffer from the buffer queue. If no free buffers
* are available this function blocks until it can provide one.
*/
static struct wayland_shm_buffer *wayland_buffer_queue_get_free_buffer(struct wayland_buffer_queue *queue)
{
struct wayland_shm_buffer *shm_buffer;
TRACE("queue=%p\n", queue);
while (TRUE)
{
int nbuffers = 0;
/* Dispatch any pending buffer release events. */
wl_display_dispatch_queue_pending(process_wayland.wl_display,
queue->wl_event_queue);
/* Search through our buffers to find an available one. */
wl_list_for_each(shm_buffer, &queue->buffer_list, link)
{
if (!shm_buffer->busy) goto out;
nbuffers++;
}
/* Dynamically create up to 3 buffers. */
if (nbuffers < 3)
{
shm_buffer = wayland_shm_buffer_create(queue->width, queue->height,
WL_SHM_FORMAT_XRGB8888);
if (shm_buffer)
{
/* Buffer events go to their own queue so that we can dispatch
* them independently. */
wl_proxy_set_queue((struct wl_proxy *) shm_buffer->wl_buffer,
queue->wl_event_queue);
wl_buffer_add_listener(shm_buffer->wl_buffer, &buffer_listener,
shm_buffer);
wl_list_insert(&queue->buffer_list, &shm_buffer->link);
goto out;
}
else if (nbuffers < 2)
{
/* If we failed to allocate a new buffer, but we have at least two
* buffers busy, there is a good chance the compositor will
* eventually release one of them, so dispatch events and wait
* below. Otherwise, give up and return a NULL buffer. */
ERR(" => failed to acquire buffer\n");
return NULL;
}
}
/* We don't have any buffers available, so block waiting for a buffer
* release event. */
if (wl_display_dispatch_queue(process_wayland.wl_display,
queue->wl_event_queue) == -1)
{
return NULL;
}
}
out:
TRACE(" => %p %dx%d map=[%p, %p)\n",
shm_buffer, shm_buffer->width, shm_buffer->height, shm_buffer->map_data,
(unsigned char*)shm_buffer->map_data + shm_buffer->map_size);
return shm_buffer;
}
/***********************************************************************
* wayland_window_surface_lock
*/
@ -119,33 +260,30 @@ static void wayland_window_surface_set_region(struct window_surface *window_surf
static void wayland_window_surface_flush(struct window_surface *window_surface)
{
struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
struct wayland_shm_buffer *shm_buffer;
struct wayland_shm_buffer *shm_buffer = NULL;
BOOL flushed = FALSE;
wayland_window_surface_lock(window_surface);
if (IsRectEmpty(&wws->bounds)) goto done;
if (!wws->wayland_surface)
if (!wws->wayland_surface || !wws->wayland_buffer_queue)
{
ERR("missing wayland surface, returning\n");
ERR("missing wayland surface=%p or buffer_queue=%p, returning\n",
wws->wayland_surface, wws->wayland_buffer_queue);
goto done;
}
TRACE("surface=%p hwnd=%p surface_rect=%s bounds=%s\n", wws, wws->hwnd,
wine_dbgstr_rect(&wws->header.rect), wine_dbgstr_rect(&wws->bounds));
shm_buffer = wayland_shm_buffer_create(wws->info.bmiHeader.biWidth,
abs(wws->info.bmiHeader.biHeight),
WL_SHM_FORMAT_XRGB8888);
shm_buffer = wayland_buffer_queue_get_free_buffer(wws->wayland_buffer_queue);
if (!shm_buffer)
{
ERR("failed to create Wayland SHM buffer, returning\n");
ERR("failed to acquire Wayland SHM buffer, returning\n");
goto done;
}
wl_buffer_add_listener(shm_buffer->wl_buffer, &buffer_listener, shm_buffer);
memcpy(shm_buffer->map_data, wws->bits, shm_buffer->map_size);
pthread_mutex_lock(&wws->wayland_surface->mutex);
@ -158,7 +296,6 @@ static void wayland_window_surface_flush(struct window_surface *window_surface)
else
{
TRACE("Wayland surface not configured yet, not flushing\n");
wayland_shm_buffer_destroy(shm_buffer);
}
pthread_mutex_unlock(&wws->wayland_surface->mutex);
wl_display_flush(process_wayland.wl_display);
@ -178,6 +315,8 @@ static void wayland_window_surface_destroy(struct window_surface *window_surface
TRACE("surface=%p\n", wws);
pthread_mutex_destroy(&wws->mutex);
if (wws->wayland_buffer_queue)
wayland_buffer_queue_destroy(wws->wayland_buffer_queue);
free(wws->bits);
free(wws);
}
@ -254,5 +393,18 @@ void wayland_window_surface_update_wayland_surface(struct window_surface *window
wws->wayland_surface = wayland_surface;
/* We only need a buffer queue if we have a surface to commit to. */
if (wws->wayland_surface && !wws->wayland_buffer_queue)
{
wws->wayland_buffer_queue =
wayland_buffer_queue_create(wws->info.bmiHeader.biWidth,
abs(wws->info.bmiHeader.biHeight));
}
else if (!wws->wayland_surface && wws->wayland_buffer_queue)
{
wayland_buffer_queue_destroy(wws->wayland_buffer_queue);
wws->wayland_buffer_queue = NULL;
}
wayland_window_surface_unlock(window_surface);
}