window: implement shm triple-buffering

Increase the maximum number of shm "leaves" to three, and rewrite the
leaf release and pick algorithms. The new algorithms hopefully improve
on buffer re-use while freeing unused buffers.

The goal of the new release algorithm is to always leave one free leaf
with storage allocated, so that the next redraw could start straight on
it.

The new leaf picking algorithm will prefer a free leaf that already has
some storage allocated, instead of just picking the first free leaf that
may need to allocate a new buffer.

Triple-buffering is especially for sub-surfaces, where the compositor
may have one wl_buffer busy on screen, and another wl_buffer busy in the
sub-surface cached state due to the synchronized commit mode. To be
able to forcibly repaint at that situation for e.g. resize, we need a
third buffer.

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

@ -820,6 +820,8 @@ shm_surface_leaf_release(struct shm_surface_leaf *leaf)
memset(leaf, 0, sizeof *leaf);
}
#define MAX_LEAVES 3
struct shm_surface {
struct toysurface base;
struct display *display;
@ -827,7 +829,7 @@ struct shm_surface {
uint32_t flags;
int dx, dy;
struct shm_surface_leaf leaf[2];
struct shm_surface_leaf leaf[MAX_LEAVES];
struct shm_surface_leaf *current;
};
@ -841,16 +843,32 @@ static void
shm_surface_buffer_release(void *data, struct wl_buffer *buffer)
{
struct shm_surface *surface = data;
struct shm_surface_leaf *leaf;
int i;
int free_found;
if (surface->leaf[0].data->buffer == buffer)
surface->leaf[0].busy = 0;
else if (surface->leaf[1].data->buffer == buffer)
surface->leaf[1].busy = 0;
else
assert(0 && "shm_surface_buffer_release: unknown buffer");
for (i = 0; i < MAX_LEAVES; i++) {
leaf = &surface->leaf[i];
if (leaf->data && leaf->data->buffer == buffer) {
leaf->busy = 0;
break;
}
}
assert(i < MAX_LEAVES && "unknown buffer released");
/* Leave one free leaf with storage, release others */
free_found = 0;
for (i = 0; i < MAX_LEAVES; i++) {
leaf = &surface->leaf[i];
if (!leaf->cairo_surface || leaf->busy)
continue;
if (!surface->leaf[0].busy && !surface->leaf[1].busy)
shm_surface_leaf_release(&surface->leaf[1]);
if (!free_found)
free_found = 1;
else
shm_surface_leaf_release(leaf);
}
}
static const struct wl_buffer_listener shm_surface_buffer_listener = {
@ -864,25 +882,22 @@ shm_surface_prepare(struct toysurface *base, int dx, int dy,
int resize_hint = !!(flags & SURFACE_HINT_RESIZE);
struct shm_surface *surface = to_shm_surface(base);
struct rectangle rect = { 0, 0, width, height };
struct shm_surface_leaf *leaf;
struct shm_surface_leaf *leaf = NULL;
int i;
surface->dx = dx;
surface->dy = dy;
/* See shm_surface_buffer_release() */
if (!surface->leaf[0].busy && !surface->leaf[1].busy &&
surface->leaf[1].cairo_surface) {
fprintf(stderr, "window.c:%s: TODO: release leaf[1]\n",
__func__);
}
/* pick a free buffer, preferrably one that already has storage */
for (i = 0; i < MAX_LEAVES; i++) {
if (surface->leaf[i].busy)
continue;
/* 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",
if (!leaf || surface->leaf[i].cairo_surface)
leaf = &surface->leaf[i];
}
if (!leaf) {
fprintf(stderr, "%s: all buffers are held by the server.\n",
__func__);
return NULL;
}
@ -964,9 +979,10 @@ static void
shm_surface_destroy(struct toysurface *base)
{
struct shm_surface *surface = to_shm_surface(base);
int i;
shm_surface_leaf_release(&surface->leaf[0]);
shm_surface_leaf_release(&surface->leaf[1]);
for (i = 0; i < MAX_LEAVES; i++)
shm_surface_leaf_release(&surface->leaf[i]);
free(surface);
}

Loading…
Cancel
Save