[PATCH] window: use only one shm pool for all the cursor images

Ander Conselvan de Oliveira conselvan2 at gmail.com
Mon May 7 11:34:26 PDT 2012


Cursor images are fairly small and having one pool for each image adds
a lot of unnecessary overhead. Instead, create one large pool and
allocated all cursor images from that.

In order to do that, however, the code that creates shm surface needed
some refactoring. This patch adds a new struct shm_pool that is used
by the cursor and also changes struct window to use it.
---
 clients/window.c |  199 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 164 insertions(+), 35 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index 2dcf421..531cedc 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -66,6 +66,7 @@
 #include "window.h"
 
 struct cursor;
+struct shm_pool;
 
 struct display {
 	struct wl_display *display;
@@ -95,6 +96,7 @@ struct display {
 	int frame_radius;
 	struct xkb_desc *xkb;
 	struct cursor *cursors;
+	struct shm_pool *cursor_shm_pool;
 
 	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
 	PFNEGLCREATEIMAGEKHRPROC create_image;
@@ -138,9 +140,7 @@ struct window {
 
 	cairo_surface_t *cairo_surface;
 
-	struct wl_shm_pool *pool;
-	size_t pool_size;
-	void *pool_data;
+	struct shm_pool *pool;
 
 	window_key_handler_t key_handler;
 	window_keyboard_focus_handler_t keyboard_focus_handler;
@@ -231,6 +231,13 @@ struct cursor {
 	struct cursor_image *images;
 };
 
+struct shm_pool {
+	struct wl_shm_pool *pool;
+	size_t size;
+	size_t used;
+	void *data;
+};
+
 enum {
 	POINTER_DEFAULT = 100,
 	POINTER_UNSET
@@ -346,18 +353,20 @@ display_get_buffer_for_surface(struct display *display,
 
 struct shm_surface_data {
 	struct surface_data data;
-	void *map;
-	size_t length;
+	struct shm_pool *pool;
 };
 
 static void
+shm_pool_destroy(struct shm_pool *pool);
+
+static void
 shm_surface_data_destroy(void *p)
 {
 	struct shm_surface_data *data = p;
 
 	wl_buffer_destroy(data->data.buffer);
-	if (data->map)
-		munmap(data->map, data->length);
+	if (data->pool)
+		shm_pool_destroy(data->pool);
 }
 
 static void
@@ -402,17 +411,74 @@ make_shm_pool(struct display *display, int size, void **data)
 	return pool;
 }
 
+static struct shm_pool *
+shm_pool_create(struct display *display, size_t size)
+{
+	struct shm_pool *pool = malloc(sizeof *pool);
+
+	if (!pool)
+		return NULL;
+
+	pool->pool = make_shm_pool(display, size, &pool->data);
+	if (!pool->pool) {
+		free(pool);
+		return NULL;
+	}
+
+	pool->size = size;
+	pool->used = 0;
+
+	return pool;
+}
+
+static void *
+shm_pool_allocate(struct shm_pool *pool, size_t size, int *offset)
+{
+	if (pool->used + size > pool->size)
+		return NULL;
+
+	*offset = pool->used;
+	pool->used += size;
+
+	return (char *) pool->data + *offset;
+}
+
+/* destroy the pool. this does not unmap the memory though */
+static void
+shm_pool_destroy(struct shm_pool *pool)
+{
+	munmap(pool->data, pool->size);
+	wl_shm_pool_destroy(pool->pool);
+	free(pool);
+}
+
+/* Start allocating from the beginning of the pool again */
+static void
+shm_pool_reset(struct shm_pool *pool)
+{
+	pool->used = 0;
+}
+
+static int
+data_length_for_shm_surface(struct rectangle *rect)
+{
+	int stride;
+
+	stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
+						rect->width);
+	return stride * rect->height;
+}
+
 static cairo_surface_t *
-display_create_shm_surface(struct display *display,
-			   struct rectangle *rectangle, uint32_t flags,
-			   struct window *window)
+display_create_shm_surface_from_pool(struct display *display,
+				     struct rectangle *rectangle,
+				     uint32_t flags, struct shm_pool *pool)
 {
 	struct shm_surface_data *data;
-	struct wl_shm_pool *pool = NULL;
 	uint32_t format;
 	cairo_surface_t *surface;
+	int stride, length, offset;
 	void *map;
-	int stride;
 
 	data = malloc(sizeof *data);
 	if (data == NULL)
@@ -420,14 +486,13 @@ display_create_shm_surface(struct display *display,
 
 	stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
 						rectangle->width);
-	data->length = stride * rectangle->height;
-	if (window && window->pool && data->length < window->pool_size) {
-		pool = window->pool;
-		map = window->pool_data;
-		data->map = NULL;
-	} else {
-		pool = make_shm_pool(display, data->length, &map);
-		data->map = map;
+	length = stride * rectangle->height;
+	data->pool = NULL;
+	map = shm_pool_allocate(pool, length, &offset);
+
+	if (!map) {
+		free(data);
+		return NULL;
 	}
 
 	surface = cairo_image_surface_create_for_data (map,
@@ -444,13 +509,50 @@ display_create_shm_surface(struct display *display,
 	else
 		format = WL_SHM_FORMAT_ARGB8888;
 
-	data->data.buffer = wl_shm_pool_create_buffer(pool, 0,
+	data->data.buffer = wl_shm_pool_create_buffer(pool->pool, offset,
 						      rectangle->width,
 						      rectangle->height,
 						      stride, format);
 
-	if (data->map)
-		wl_shm_pool_destroy(pool);
+	return surface;
+}
+
+static cairo_surface_t *
+display_create_shm_surface(struct display *display,
+			   struct rectangle *rectangle, uint32_t flags,
+			   struct window *window)
+{
+	struct shm_surface_data *data;
+	struct shm_pool *pool;
+	cairo_surface_t *surface;
+
+	if (window && window->pool) {
+		shm_pool_reset(window->pool);
+		surface = display_create_shm_surface_from_pool(display,
+							       rectangle,
+							       flags,
+							       window->pool);
+		if (surface)
+			return surface;
+	}
+
+	pool = shm_pool_create(display,
+			       data_length_for_shm_surface(rectangle));
+	if (!pool)
+		return NULL;
+
+	surface =
+		display_create_shm_surface_from_pool(display, rectangle,
+						     flags, pool);
+
+	if (!surface) {
+		shm_pool_destroy(pool);
+		return NULL;
+	}
+
+	/* make sure we destroy the pool when the surface is destroyed */
+	data = cairo_surface_get_user_data(surface, &surface_data_key);
+	data->pool = pool;
 
 	return surface;
 }
@@ -517,7 +619,8 @@ create_cursor_from_images(struct display *display, struct cursor *cursor,
 		rect.height = image->height;
 
 		cursor->images[i].surface =
-			display_create_shm_surface(display, &rect, 0, NULL);
+			display_create_shm_surface_from_pool(display, &rect, 0,
+							     display->cursor_shm_pool);
 
 		shm_surface_write(cursor->images[i].surface,
 				  (unsigned char *) image->pixels,
@@ -532,30 +635,57 @@ create_cursor_from_images(struct display *display, struct cursor *cursor,
 
 }
 
+static size_t
+data_length_for_cursor_images(XcursorImages *images)
+{
+	int i;
+	size_t length = 0;
+	struct rectangle rect;
+
+	for (i = 0; i < images->nimage; i++) {
+		rect.width = images->images[i]->width;
+		rect.height = images->images[i]->height;
+		length += data_length_for_shm_surface(&rect);
+	}
+
+	return length;
+}
+
 static void
 create_cursors(struct display *display)
 {
 	int i, count;
+	size_t pool_size = 0;
 	struct cursor *cursor;
-	XcursorImages *images;
+	XcursorImages **images;
 
 	count = ARRAY_LENGTH(cursors);
 	display->cursors = malloc(count * sizeof *display->cursors);
-	for (i = 0; i < count; i++) {
-		images = XcursorLibraryLoadImages(cursors[i], NULL, 32);
+	images = malloc(count * sizeof images[0]);
 
+	for (i = 0; i < count; i++) {
+		images[i] = XcursorLibraryLoadImages(cursors[i], NULL, 32);
 		if (!images) {
 			fprintf(stderr, "Error loading cursor: %s\n",
 				cursors[i]);
 			continue;
 		}
+		pool_size += data_length_for_cursor_images(images[i]);
+	}
+
+	display->cursor_shm_pool = shm_pool_create(display, pool_size);
+
+	for (i = 0; i < count; i++) {
+		if (!images)
+			continue;
 
 		cursor = &display->cursors[i];
-		create_cursor_from_images(display, cursor, images);
+		create_cursor_from_images(display, cursor, images[i]);
 
-		XcursorImagesDestroy(images);
+		XcursorImagesDestroy(images[i]);
 	}
 
+	free(images);
 }
 
 static void
@@ -579,7 +709,9 @@ destroy_cursors(struct display *display)
 	for (i = 0; i < count; ++i) {
 		destroy_cursor_images(&display->cursors[i]);
 	}
+
 	free(display->cursors);
+	shm_pool_destroy(display->cursor_shm_pool);
 }
 
 cairo_surface_t *
@@ -1258,10 +1390,8 @@ frame_button_handler(struct widget *widget,
 				   pool to create buffers out of while
 				   we resize.  We should probably base
 				   this number on the size of the output. */
-				window->pool_size = 6 * 1024 * 1024;
-				window->pool = make_shm_pool(display,
-							     window->pool_size,
-							     &window->pool_data);
+				window->pool =
+					shm_pool_create(display, 6 * 1024 * 1024);
 			}
 
 			wl_shell_surface_resize(window->shell_surface,
@@ -1486,8 +1616,7 @@ input_handle_pointer_enter(void *data,
 	window = input->pointer_focus;
 
 	if (window->pool) {
-		wl_shm_pool_destroy(window->pool);
-		munmap(window->pool_data, window->pool_size);
+		shm_pool_destroy(window->pool);
 		window->pool = NULL;
 		/* Schedule a redraw to free the pool */
 		window_schedule_redraw(window);
-- 
1.7.9.5



More information about the wayland-devel mailing list