From: Alexandros Frantzis alexandros.frantzis@collabora.com
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. --- dlls/winewayland.drv/wayland_surface.c | 23 ++- dlls/winewayland.drv/waylanddrv.h | 6 +- dlls/winewayland.drv/window_surface.c | 193 +++++++++++++++++++++++-- 3 files changed, 204 insertions(+), 18 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index ad30745fbc5..e200be769f7 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -234,13 +234,25 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, }
/********************************************************************** - * 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 +296,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 +353,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; } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 0afc03f4147..5abc5ae2d43 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -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 diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index ca613c667c6..76084a7c8ad 100644 --- a/dlls/winewayland.drv/window_surface.c +++ b/dlls/winewayland.drv/window_surface.c @@ -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,149 @@ 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_acquire_buffer + * + * Acquires a free buffer from the buffer queue. If no free buffers + * are available this function blocks until it can provide one. + * + * The returned buffer is marked as unavailable until committed to + * a surface and subsequently released by the compositor. + */ +static struct wayland_shm_buffer *wayland_buffer_queue_acquire_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); + + shm_buffer->busy = TRUE; + wayland_shm_buffer_ref(shm_buffer); + + return shm_buffer; +} + /*********************************************************************** * wayland_window_surface_lock */ @@ -119,33 +266,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_acquire_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,13 +302,23 @@ 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);
done: - if (flushed) reset_bounds(&wws->bounds); + if (flushed) + { + reset_bounds(&wws->bounds); + } + else + { + if (shm_buffer) + { + shm_buffer->busy = FALSE; + wayland_shm_buffer_unref(shm_buffer); + } + } wayland_window_surface_unlock(window_surface); }
@@ -178,6 +332,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 +410,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); }