mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-15 06:41:41 +00:00
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:
parent
6bf3b5539a
commit
4371a26041
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue