window: honour wl_buffer.release

Listen for wl_buffer.release events in the shm path, and if a previously
posted buffer is still held by the server, allocate another one. The
maximum of two should be enough, since there is no point for a server to
hold more than one buffer at a time.

Buffer allocation happens as needed instead of window creation time.

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
Pekka Paalanen 12 years ago committed by Kristian Høgsberg
parent 3cbb08944b
commit a4eda73607
  1. 113
      clients/window.c

@ -782,6 +782,26 @@ display_create_surface(struct display *display,
NULL, NULL);
}
struct shm_surface_leaf {
cairo_surface_t *cairo_surface;
/* 'data' is automatically destroyed, when 'cairo_surface' is */
struct shm_surface_data *data;
struct shm_pool *resize_pool;
int busy;
};
static void
shm_surface_leaf_release(struct shm_surface_leaf *leaf)
{
if (leaf->cairo_surface)
cairo_surface_destroy(leaf->cairo_surface);
/* leaf->data already destroyed via cairo private */
if (leaf->resize_pool)
shm_pool_destroy(leaf->resize_pool);
}
struct shm_surface {
struct toysurface base;
struct display *display;
@ -789,11 +809,8 @@ struct shm_surface {
uint32_t flags;
int dx, dy;
cairo_surface_t *cairo_surface;
/* 'data' is automatically destroyed, when 'cairo_surface' is */
struct shm_surface_data *data;
struct shm_pool *resize_pool;
struct shm_surface_leaf leaf[2];
struct shm_surface_leaf *current;
};
static struct shm_surface *
@ -802,50 +819,78 @@ to_shm_surface(struct toysurface *base)
return container_of(base, struct shm_surface, base);
}
static void
shm_surface_buffer_release(void *data, struct wl_buffer *buffer)
{
struct shm_surface_leaf *leaf = data;
leaf->busy = 0;
}
static const struct wl_buffer_listener shm_surface_buffer_listener = {
shm_surface_buffer_release
};
static cairo_surface_t *
shm_surface_prepare(struct toysurface *base, int dx, int dy,
int width, int height, int resize_hint)
{
struct shm_surface *surface = to_shm_surface(base);
struct rectangle rect = { 0, 0, width, height };
struct shm_surface_leaf *leaf;
surface->dx = dx;
surface->dy = dy;
if (!resize_hint && surface->resize_pool) {
cairo_surface_destroy(surface->cairo_surface);
shm_pool_destroy(surface->resize_pool);
surface->resize_pool = NULL;
surface->cairo_surface = NULL;
/* pick a free buffer from the two */
if (!surface->leaf[0].busy)
leaf = &surface->leaf[0];
else if (!surface->leaf[1].busy)
leaf = &surface->leaf[1];
else {
fprintf(stderr, "%s: both buffers are held by the server.\n",
__func__);
return NULL;
}
if (!resize_hint && leaf->resize_pool) {
cairo_surface_destroy(leaf->cairo_surface);
leaf->cairo_surface = NULL;
shm_pool_destroy(leaf->resize_pool);
leaf->resize_pool = NULL;
}
if (surface->cairo_surface &&
cairo_image_surface_get_width(surface->cairo_surface) == width &&
cairo_image_surface_get_height(surface->cairo_surface) == height)
if (leaf->cairo_surface &&
cairo_image_surface_get_width(leaf->cairo_surface) == width &&
cairo_image_surface_get_height(leaf->cairo_surface) == height)
goto out;
if (surface->cairo_surface)
cairo_surface_destroy(surface->cairo_surface);
if (leaf->cairo_surface)
cairo_surface_destroy(leaf->cairo_surface);
if (resize_hint && !surface->resize_pool) {
if (resize_hint && !leaf->resize_pool) {
/* Create a big pool to allocate from, while continuously
* resizing. Mmapping a new pool in the server
* is relatively expensive, so reusing a pool performs
* better, but may temporarily reserve unneeded memory.
*/
/* We should probably base this number on the output size. */
surface->resize_pool = shm_pool_create(surface->display,
6 * 1024 * 1024);
leaf->resize_pool = shm_pool_create(surface->display,
6 * 1024 * 1024);
}
surface->cairo_surface =
leaf->cairo_surface =
display_create_shm_surface(surface->display, &rect,
surface->flags,
surface->resize_pool,
&surface->data);
leaf->resize_pool,
&leaf->data);
wl_buffer_add_listener(leaf->data->buffer,
&shm_surface_buffer_listener, leaf);
out:
return cairo_surface_reference(surface->cairo_surface);
surface->current = leaf;
return cairo_surface_reference(leaf->cairo_surface);
}
static void
@ -853,17 +898,21 @@ shm_surface_swap(struct toysurface *base,
struct rectangle *server_allocation)
{
struct shm_surface *surface = to_shm_surface(base);
struct shm_surface_leaf *leaf = surface->current;
server_allocation->width =
cairo_image_surface_get_width(surface->cairo_surface);
cairo_image_surface_get_width(leaf->cairo_surface);
server_allocation->height =
cairo_image_surface_get_height(surface->cairo_surface);
cairo_image_surface_get_height(leaf->cairo_surface);
wl_surface_attach(surface->surface, surface->data->buffer,
wl_surface_attach(surface->surface, leaf->data->buffer,
surface->dx, surface->dy);
wl_surface_damage(surface->surface, 0, 0,
server_allocation->width, server_allocation->height);
wl_surface_commit(surface->surface);
leaf->busy = 1;
surface->current = NULL;
}
static int
@ -882,11 +931,8 @@ shm_surface_destroy(struct toysurface *base)
{
struct shm_surface *surface = to_shm_surface(base);
/* this destroys surface->data, too */
cairo_surface_destroy(surface->cairo_surface);
if (surface->resize_pool)
shm_pool_destroy(surface->resize_pool);
shm_surface_leaf_release(&surface->leaf[0]);
shm_surface_leaf_release(&surface->leaf[1]);
free(surface);
}
@ -910,13 +956,6 @@ shm_surface_create(struct display *display, struct wl_surface *wl_surface,
surface->display = display;
surface->surface = wl_surface;
surface->flags = flags;
surface->cairo_surface = display_create_shm_surface(display, rectangle,
flags, NULL,
&surface->data);
if (!surface->cairo_surface) {
free(surface);
return NULL;
}
return &surface->base;
}

Loading…
Cancel
Save