[PATCH weston] simple-shm: honour wl_buffer.release

Pekka Paalanen ppaalanen at gmail.com
Mon Nov 19 05:29:09 PST 2012


Change simple-shm to properly process the wl_buffer.release event, and
not reuse a buffer until it is released by the server, as specified in
the protocol.

In case the server has not released the buffer, but signals that it has
been shown (frame callback), allocate a second buffer. Simple-shm will
now automatically do double-buffering if needed.

Signed-off-by: Pekka Paalanen <ppaalanen at gmail.com>
---
 clients/simple-shm.c |  113 ++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 87 insertions(+), 26 deletions(-)

diff --git a/clients/simple-shm.c b/clients/simple-shm.c
index a09ec91..5bc26bf 100644
--- a/clients/simple-shm.c
+++ b/clients/simple-shm.c
@@ -42,22 +42,39 @@ struct display {
 	uint32_t formats;
 };
 
+struct buffer {
+	struct wl_buffer *buffer;
+	void *shm_data;
+	int busy;
+};
+
 struct window {
 	struct display *display;
 	int width, height;
 	struct wl_surface *surface;
 	struct wl_shell_surface *shell_surface;
-	struct wl_buffer *buffer;
-	void *shm_data;
+	struct buffer buffers[2];
+	struct buffer *prev_buffer;
 	struct wl_callback *callback;
 };
 
-static struct wl_buffer *
-create_shm_buffer(struct display *display,
-		  int width, int height, uint32_t format, void **data_out)
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+	struct buffer *mybuf = data;
+
+	mybuf->busy = 0;
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+	buffer_release
+};
+
+static int
+create_shm_buffer(struct display *display, struct buffer *buffer,
+		  int width, int height, uint32_t format)
 {
 	struct wl_shm_pool *pool;
-	struct wl_buffer *buffer;
 	int fd, size, stride;
 	void *data;
 
@@ -68,25 +85,27 @@ create_shm_buffer(struct display *display,
 	if (fd < 0) {
 		fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
 			size);
-		return NULL;
+		return -1;
 	}
 
 	data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 	if (data == MAP_FAILED) {
 		fprintf(stderr, "mmap failed: %m\n");
 		close(fd);
-		return NULL;
+		return -1;
 	}
 
 	pool = wl_shm_create_pool(display->shm, fd, size);
-	buffer = wl_shm_pool_create_buffer(pool, 0,
-					   width, height, stride, format);
+	buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
+						   width, height,
+						   stride, format);
+	wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
 	wl_shm_pool_destroy(pool);
 	close(fd);
 
-	*data_out = data;
+	buffer->shm_data = data;
 
-	return buffer;
+	return 0;
 }
 
 static void
@@ -117,18 +136,10 @@ static struct window *
 create_window(struct display *display, int width, int height)
 {
 	struct window *window;
-	
-	window = malloc(sizeof *window);
-
-	window->buffer = create_shm_buffer(display,
-					   width, height,
-					   WL_SHM_FORMAT_XRGB8888,
-					   &window->shm_data);
 
-	if (!window->buffer) {
-		free(window);
+	window = calloc(1, sizeof *window);
+	if (!window)
 		return NULL;
-	}
 
 	window->callback = NULL;
 	window->display = display;
@@ -155,12 +166,45 @@ destroy_window(struct window *window)
 	if (window->callback)
 		wl_callback_destroy(window->callback);
 
-	wl_buffer_destroy(window->buffer);
+	if (window->buffers[0].buffer)
+		wl_buffer_destroy(window->buffers[0].buffer);
+	if (window->buffers[1].buffer)
+		wl_buffer_destroy(window->buffers[1].buffer);
+
 	wl_shell_surface_destroy(window->shell_surface);
 	wl_surface_destroy(window->surface);
 	free(window);
 }
 
+static struct buffer *
+window_next_buffer(struct window *window)
+{
+	struct buffer *buffer;
+	int ret = 0;
+
+	if (!window->buffers[0].busy)
+		buffer = &window->buffers[0];
+	else if (!window->buffers[1].busy)
+		buffer = &window->buffers[1];
+	else
+		return NULL;
+
+	if (!buffer->buffer) {
+		ret = create_shm_buffer(window->display, buffer,
+					window->width, window->height,
+					WL_SHM_FORMAT_XRGB8888);
+
+		if (ret < 0)
+			return NULL;
+
+		/* paint the padding */
+		memset(buffer->shm_data, 0xff,
+		       window->width * window->height * 4);
+	}
+
+	return buffer;
+}
+
 static void
 paint_pixels(void *image, int padding, int width, int height, uint32_t time)
 {
@@ -213,8 +257,23 @@ static void
 redraw(void *data, struct wl_callback *callback, uint32_t time)
 {
 	struct window *window = data;
+	struct buffer *buffer;
+
+	buffer = window_next_buffer(window);
+	if (!buffer) {
+		fprintf(stderr,
+			!callback ? "Failed to create the first buffer.\n" :
+			"Both buffers busy at redraw(). Server bug?\n");
+		abort();
+	}
+
+	paint_pixels(buffer->shm_data, 20, window->width, window->height, time);
+
+	if (window->prev_buffer != buffer) {
+		wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+		window->prev_buffer = buffer;
+	}
 
-	paint_pixels(window->shm_data, 20, window->width, window->height, time);
 	wl_surface_damage(window->surface,
 			  20, 20, window->width - 40, window->height - 40);
 
@@ -224,6 +283,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
 	window->callback = wl_surface_frame(window->surface);
 	wl_callback_add_listener(window->callback, &frame_listener, window);
 	wl_surface_commit(window->surface);
+	buffer->busy = 1;
 }
 
 static const struct wl_callback_listener frame_listener = {
@@ -340,8 +400,9 @@ main(int argc, char **argv)
 	sigint.sa_flags = SA_RESETHAND;
 	sigaction(SIGINT, &sigint, NULL);
 
-	memset(window->shm_data, 0xff, window->width * window->height * 4);
-	wl_surface_attach(window->surface, window->buffer, 0, 0);
+	/* Initialise damage to full surface, so the padding gets painted */
+	wl_surface_damage(window->surface, 0, 0,
+			  window->width, window->height);
 
 	redraw(window, NULL, 0);
 
-- 
1.7.8.6



More information about the wayland-devel mailing list