From 5b51c8c6f0735d93949c4faa4be3632c0a66c67e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 17 Jul 2009 18:32:40 +0200 Subject: [PATCH] optimization: Render on pixmaps and copy the result on Stack_Wins This should speed up the rendering of Stack_Wins with many window decorations and it should considerably reduce flicker. --- include/data.h | 18 +++++++++++++++++- include/i3.h | 1 + include/xcb.h | 9 +++++++++ src/layout.c | 8 +++++++- src/mainx.c | 7 ++++++- src/util.c | 11 +++++++---- src/xcb.c | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 80 insertions(+), 7 deletions(-) diff --git a/include/data.h b/include/data.h index 2776d506..3288cb8d 100644 --- a/include/data.h +++ b/include/data.h @@ -101,6 +101,22 @@ struct Colorpixel { SLIST_ENTRY(Colorpixel) colorpixels; }; +struct Cached_Pixmap { + xcb_pixmap_t id; + + /* We’re going to paint on it, so a graphics context will be needed */ + xcb_gcontext_t gc; + + /* The rect with which the pixmap was created */ + Rect rect; + + /* The rect of the object to which this pixmap belongs. Necessary to + * find out when we need to re-create the pixmap. */ + Rect *referred_rect; + + xcb_drawable_t referred_drawable; +}; + /** * Contains data for the windows needed to draw the titlebars on in stacking * mode @@ -108,7 +124,7 @@ struct Colorpixel { */ struct Stack_Window { xcb_window_t window; - xcb_gcontext_t gc; + struct Cached_Pixmap pixmap; Rect rect; /** Backpointer to the container this stack window is in */ diff --git a/include/i3.h b/include/i3.h index ccf7a495..a489b42f 100644 --- a/include/i3.h +++ b/include/i3.h @@ -30,6 +30,7 @@ extern TAILQ_HEAD(assignments_head, Assignment) assignments; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; extern xcb_event_handlers_t evenths; extern int num_screens; +extern uint8_t root_depth; extern xcb_atom_t atoms[NUM_ATOMS]; #endif diff --git a/include/xcb.h b/include/xcb.h index de74bab6..b540a5cc 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -143,4 +143,13 @@ void xcb_get_numlock_mask(xcb_connection_t *conn); */ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window); +/** + * + * Prepares the given Cached_Pixmap for usage (checks whether the size of the + * object this pixmap is related to (e.g. a window) has changed and re-creates + * the pixmap if so). + * + */ +void cached_pixmap_prepare(xcb_connection_t *conn, struct Cached_Pixmap *pixmap); + #endif diff --git a/src/layout.c b/src/layout.c index 4f132aea..89c561ea 100644 --- a/src/layout.c +++ b/src/layout.c @@ -362,6 +362,9 @@ void render_container(xcb_connection_t *conn, Container *container) { xcb_configure_window(conn, stack_win->window, mask, values); } + /* Prepare the pixmap for usage */ + cached_pixmap_prepare(conn, &(stack_win->pixmap)); + /* Render the decorations of all clients */ CIRCLEQ_FOREACH(client, &(container->clients), clients) { /* If the client is in fullscreen mode, it does not get reconfigured */ @@ -384,9 +387,12 @@ void render_container(xcb_connection_t *conn, Container *container) { client->force_reconfigure = false; - decorate_window(conn, client, stack_win->window, stack_win->gc, + decorate_window(conn, client, stack_win->pixmap.id, stack_win->pixmap.gc, current_client++ * decoration_height); } + + xcb_copy_area(conn, stack_win->pixmap.id, stack_win->window, stack_win->pixmap.gc, + 0, 0, 0, 0, stack_win->rect.width, stack_win->rect.height); } } diff --git a/src/mainx.c b/src/mainx.c index d95ca465..6061fd8d 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -71,6 +71,9 @@ xcb_atom_t atoms[NUM_ATOMS]; int num_screens = 0; +/* The depth of the root screen (used e.g. for creating new pixmaps later) */ +uint8_t root_depth; + /* * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop @@ -261,7 +264,9 @@ int main(int argc, char *argv[], char *env[]) { xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL); /* Get the root window and set the event mask */ - root = xcb_aux_get_screen(conn, screens)->root; + xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root = root_screen->root; + root_depth = root_screen->root_depth; uint32_t mask = XCB_CW_EVENT_MASK; uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | diff --git a/src/util.c b/src/util.c index 3a3890f1..02ce5496 100644 --- a/src/util.c +++ b/src/util.c @@ -385,7 +385,8 @@ void leave_stack_mode(xcb_connection_t *conn, Container *container) { SLIST_REMOVE(&stack_wins, stack_win, Stack_Window, stack_windows); - xcb_free_gc(conn, stack_win->gc); + xcb_free_gc(conn, stack_win->pixmap.gc); + xcb_free_pixmap(conn, stack_win->pixmap.id); xcb_destroy_window(conn, stack_win->window); stack_win->rect.width = -1; @@ -423,9 +424,11 @@ void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode) struct Stack_Window *stack_win = &(container->stack_win); stack_win->window = create_window(conn, rect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, mask, values); - /* Generate a graphics context for the titlebar */ - stack_win->gc = xcb_generate_id(conn); - xcb_create_gc(conn, stack_win->gc, stack_win->window, 0, 0); + /* Initialize the entry for our cached pixmap. It will be + * created as soon as it’s needed (see cached_pixmap_prepare). */ + memset(&(stack_win->pixmap), 0, sizeof(struct Cached_Pixmap)); + stack_win->pixmap.referred_rect = &stack_win->rect; + stack_win->pixmap.referred_drawable = stack_win->window; stack_win->container = container; diff --git a/src/xcb.c b/src/xcb.c index 4062f648..4ad69db1 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -18,6 +18,7 @@ #include #include +#include "i3.h" #include "util.h" #include "xcb.h" @@ -259,3 +260,35 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window) { uint32_t values[] = { XCB_STACK_MODE_ABOVE }; xcb_configure_window(conn, window, XCB_CONFIG_WINDOW_STACK_MODE, values); } + +/* + * + * Prepares the given Cached_Pixmap for usage (checks whether the size of the + * object this pixmap is related to (e.g. a window) has changed and re-creates + * the pixmap if so). + * + */ +void cached_pixmap_prepare(xcb_connection_t *conn, struct Cached_Pixmap *pixmap) { + LOG("preparing pixmap\n"); + + /* If the Rect did not change, the pixmap does not need to be recreated */ + if (memcmp(&(pixmap->rect), pixmap->referred_rect, sizeof(Rect)) == 0) + return; + + memcpy(&(pixmap->rect), pixmap->referred_rect, sizeof(Rect)); + + if (pixmap->id == 0 || pixmap->gc == 0) { + LOG("Creating new pixmap...\n"); + pixmap->id = xcb_generate_id(conn); + pixmap->gc = xcb_generate_id(conn); + } else { + LOG("Re-creating this pixmap...\n"); + xcb_free_gc(conn, pixmap->gc); + xcb_free_pixmap(conn, pixmap->id); + } + + xcb_create_pixmap(conn, root_depth, pixmap->id, + pixmap->referred_drawable, pixmap->rect.width, pixmap->rect.height); + + xcb_create_gc(conn, pixmap->gc, pixmap->id, 0, 0); +} -- 2.39.5