From 80eb12b7c165945b366491987c4c83ff426930bc Mon Sep 17 00:00:00 2001 From: James Magahern Date: Sat, 9 Feb 2019 14:54:37 -0800 Subject: Some potentially unnecessary performance optimizations Keep track of which "layers" are dirty using a bitfield, and only redraw the ones that are dirty. --- src/main.c | 16 +++++++-- src/render.c | 105 ++++++++++++++++++++++++++++++++++++++++++++--------------- src/render.h | 15 ++++++++- 3 files changed, 106 insertions(+), 30 deletions(-) diff --git a/src/main.c b/src/main.c index 82439e6..1b6a816 100644 --- a/src/main.c +++ b/src/main.c @@ -124,6 +124,8 @@ static void handle_key_event(saver_state_t *state, XKeyEvent *event) password_buf[length + 1] = '\0'; } } + + set_layer_needs_draw(state, LAYER_PASSWORD, true); } static int poll_events(saver_state_t *state) @@ -256,6 +258,7 @@ void callback_prompt_user(const char *prompt, void *context) set_password_prompt(state, prompt); state->input_allowed = true; state->is_processing = false; + set_layer_needs_draw(state, LAYER_PROMPT, true); } void callback_authentication_result(int result, void *context) @@ -281,9 +284,18 @@ static void update(saver_state_t *state) static void draw(saver_state_t *state) { - draw_background(state); - draw_logo(state); + if (layer_needs_draw(state, LAYER_BACKGROUND)) { + draw_background(state, 0, 0, state->canvas_width, state->canvas_height); + } + + if (layer_needs_draw(state, LAYER_LOGO)) { + draw_logo(state); + } + draw_password_field(state); + + // Automatically reset this after every draw call + set_layer_needs_draw(state, LAYER_BACKGROUND, false); } static int runloop(cairo_surface_t *surface) diff --git a/src/render.c b/src/render.c index ab1e701..4887628 100644 --- a/src/render.c +++ b/src/render.c @@ -68,6 +68,14 @@ static void update_single_animation(saver_state_t *state, animation_t *anim) state->logo_fill_progress = progress; state->password_opacity = progress; + set_layer_needs_draw(state, LAYER_LOGO, true); + if (anim->direction == OUT) { + // When transitioning OUT, background essentially draws over the logo as it wipes out + set_layer_needs_draw(state, LAYER_BACKGROUND, true); + } + + // And since the status text fades along with the logo + set_layer_needs_draw(state, LAYER_PROMPT, true); anim->completed = anim_complete(anim, progress); } @@ -90,6 +98,7 @@ static void update_single_animation(saver_state_t *state, animation_t *anim) anim->completed = completed; state->background_redshift = progress; + set_layer_needs_draw(state, LAYER_BACKGROUND, true); } // Spinner animation @@ -169,6 +178,26 @@ void update_animations(saver_state_t *state) state->num_animations -= completed_animations; } +bool layer_needs_draw(saver_state_t *state, const layer_type_t type) +{ + if (state->dirty_layers & LAYER_BACKGROUND) { + // Special case: if the background needs to be drawn, everything on top of it + // needs to as well. + return true; + } + + return (state->dirty_layers & type); +} + +void set_layer_needs_draw(saver_state_t *state, const layer_type_t type, bool needs_draw) +{ + if (needs_draw) { + state->dirty_layers |= type; + } else { + state->dirty_layers &= ~type; + } +} + RsvgHandle* load_svg_for_resource_path(const char *resource_path) { GError *error = NULL; @@ -186,12 +215,15 @@ RsvgHandle* load_svg_for_resource_path(const char *resource_path) return handle; } -void draw_background(saver_state_t *state) +void draw_background(saver_state_t *state, double x, double y, double width, double height) { // Draw background cairo_t *cr = state->ctx; + cairo_save(cr); cairo_set_source_rgba(cr, (state->background_redshift / 1.5), 0.0, 0.0, 1.0); - cairo_paint(cr); + cairo_rectangle(cr, x, y, width, height); + cairo_fill(cr); + cairo_restore(cr); } void draw_logo(saver_state_t *state) @@ -225,6 +257,8 @@ void draw_logo(saver_state_t *state) rsvg_handle_render_cairo(state->logo_svg_handle, cr); cairo_restore(cr); + + set_layer_needs_draw(state, LAYER_LOGO, false); } void draw_password_field(saver_state_t *state) @@ -267,8 +301,14 @@ void draw_password_field(saver_state_t *state) } // Draw status text - cairo_move_to(cr, spinner_width + field_x, field_y - line_height - field_padding); - pango_cairo_show_layout(cr, state->pango_layout); + if (layer_needs_draw(state, LAYER_PROMPT) || state->is_processing) { + const double y_position = field_y - line_height - field_padding; + draw_background(state, field_x, y_position, state->canvas_width - field_x, line_height); + cairo_move_to(cr, spinner_width + field_x, y_position); + pango_cairo_show_layout(cr, state->pango_layout); + + set_layer_needs_draw(state, LAYER_PROMPT, false); + } // Draw processing indicator if (state->is_processing) { @@ -292,47 +332,58 @@ void draw_password_field(saver_state_t *state) } // Draw password asterisks + const double cursor_padding_x = 10.0; if (state->asterisk_svg_handle == NULL) { state->asterisk_svg_handle = load_svg_for_resource_path("/resources/asterisk.svg"); } - const double cursor_padding_x = 10.0; - double cursor_offset_x = 0.0; - RsvgDimensionData dimensions; rsvg_handle_get_dimensions(state->asterisk_svg_handle, &dimensions); - double asterisk_height = cursor_height - 20.0; - double scale_factor = (asterisk_height / dimensions.height); - double scaled_width = (dimensions.width * scale_factor); + const double asterisk_height = cursor_height - 20.0; + const double scale_factor = (asterisk_height / dimensions.height); + const double scaled_width = (dimensions.width * scale_factor); + const double asterisk_width = (scaled_width + cursor_padding_x); + const unsigned int num_asterisks = strlen(state->password_buffer); + + if (layer_needs_draw(state, LAYER_PASSWORD)) { + // Draw background first + draw_background(state, field_x, field_y - (field_padding / 2.0), + (asterisk_width * num_asterisks), cursor_height + field_padding); + + // Asterisks are all rendered in a single group so their opacity can change (password_opacity) + cairo_push_group(cr); + double cursor_offset_x = 0.0; + for (unsigned i = 0; i < num_asterisks; i++) { + cairo_save(cr); + cairo_translate(cr, field_x + cursor_offset_x, field_y + ((cursor_height - asterisk_height) / 2.0)); + cairo_scale(cr, scale_factor, scale_factor); + rsvg_handle_render_cairo(state->asterisk_svg_handle, cr); + cairo_restore(cr); + + cursor_offset_x += asterisk_width; + } + + cairo_pattern_t *asterisk_pattern = cairo_pop_group(cr); - // Asterisks are all rendered in a single group so their opacity can change (password_opacity) - cairo_push_group(cr); - for (unsigned i = 0; i < strlen(state->password_buffer); i++) { cairo_save(cr); - cairo_translate(cr, field_x + cursor_offset_x, field_y + ((cursor_height - asterisk_height) / 2.0)); - cairo_scale(cr, scale_factor, scale_factor); - rsvg_handle_render_cairo(state->asterisk_svg_handle, cr); + cairo_set_source(cr, asterisk_pattern); + cairo_paint_with_alpha(cr, state->password_opacity); cairo_restore(cr); + cairo_pattern_destroy(asterisk_pattern); - cursor_offset_x += scaled_width + cursor_padding_x; + set_layer_needs_draw(state, LAYER_PASSWORD, false); } - cairo_pattern_t *asterisk_pattern = cairo_pop_group(cr); - - cairo_save(cr); - cairo_set_source(cr, asterisk_pattern); - cairo_paint_with_alpha(cr, state->password_opacity); - cairo_restore(cr); - cairo_pattern_destroy(asterisk_pattern); - // Draw cursor + const double x_offset = (num_asterisks * asterisk_width); cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, MIN(state->password_opacity, state->cursor_opacity)); if (!state->is_processing) { - cairo_rectangle(cr, field_x + cursor_offset_x, field_y, cursor_width, cursor_height); + draw_background(state, field_x + x_offset, field_y, state->canvas_width, cursor_height); + cairo_rectangle(cr, field_x + x_offset, field_y, cursor_width, cursor_height); } else { // Fill asterisks - cairo_rectangle(cr, field_x, field_y, cursor_offset_x, cursor_height); + cairo_rectangle(cr, field_x, field_y, x_offset, cursor_height); } cairo_fill(cr); } diff --git a/src/render.h b/src/render.h index d0de544..e80250f 100644 --- a/src/render.h +++ b/src/render.h @@ -22,6 +22,13 @@ typedef unsigned animation_key_t; #define ANIM_KEY_NOEXIST (kMaxAnimations + 1) +typedef enum { + LAYER_BACKGROUND = 1 << 0, + LAYER_PROMPT = 1 << 1, + LAYER_LOGO = 1 << 2, + LAYER_PASSWORD = 1 << 3, +} layer_type_t; + typedef struct { cairo_t *ctx; cairo_surface_t *surface; @@ -56,6 +63,8 @@ typedef struct { animation_t animations[kMaxAnimations]; unsigned num_animations; + layer_type_t dirty_layers; + struct auth_handle_t *auth_handle; } saver_state_t; @@ -75,7 +84,7 @@ animation_t* get_animation_for_key(saver_state_t *state, animation_key_t anim_ke void update_animations(saver_state_t *state); // Background -void draw_background(saver_state_t *state); +void draw_background(saver_state_t *state, double x, double y, double width, double height); // The purple sidebar void draw_logo(saver_state_t *state); @@ -83,4 +92,8 @@ void draw_logo(saver_state_t *state); // The status string and paassword field void draw_password_field(saver_state_t *state); +// Convenience function for getting layer dirty state +bool layer_needs_draw(saver_state_t *state, const layer_type_t type); +// Convenience function for setting layer dirty state +void set_layer_needs_draw(saver_state_t *state, const layer_type_t type, bool needs_draw); -- cgit v1.2.3