From 001de54ea18791ba3f1456d32a84bf3c9d9ef1fd Mon Sep 17 00:00:00 2001 From: Ander Conselvan de Oliveira Date: Mon, 7 May 2012 11:34:26 -0700 Subject: [PATCH] window: use only one shm pool for all the cursor images 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 | 205 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 167 insertions(+), 38 deletions(-) diff --git a/clients/window.c b/clients/window.c index 26571409..c7da54ac 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; } - - cursor = &display->cursors[i]; - create_cursor_from_images(display, cursor, images); - - XcursorImagesDestroy(images); + 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[i]); + + 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);