From 947fa16c8b8bf6a0f91ca7b01da8c47f8cc9361c Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Tue, 24 Nov 2009 07:17:18 +0100 Subject: [PATCH] Important speedup by not redrawing too much --- gfxmenu/gfxmenu.c | 12 +- gfxmenu/gui_box.c | 4 +- gfxmenu/gui_canvas.c | 5 +- gfxmenu/gui_circular_progress.c | 6 +- gfxmenu/gui_image.c | 8 +- gfxmenu/gui_label.c | 5 +- gfxmenu/gui_list.c | 6 +- gfxmenu/gui_progress_bar.c | 4 +- gfxmenu/view.c | 214 +++++++++++++++++++++++--------- include/grub/gfxmenu_view.h | 14 +++ include/grub/gui.h | 16 ++- 11 files changed, 221 insertions(+), 73 deletions(-) diff --git a/gfxmenu/gfxmenu.c b/gfxmenu/gfxmenu.c index 6f7382c18..ab1c8befb 100644 --- a/gfxmenu/gfxmenu.c +++ b/gfxmenu/gfxmenu.c @@ -35,6 +35,7 @@ #include #include #include +#include static void switch_to_text_menu (void) { @@ -62,6 +63,7 @@ process_key_press (int c, { i++; grub_gfxmenu_model_set_selected_index (model, i); + grub_gfxmenu_redraw_menu (view); } } break; @@ -74,6 +76,7 @@ process_key_press (int c, { i--; grub_gfxmenu_model_set_selected_index (model, i); + grub_gfxmenu_redraw_menu (view); } } break; @@ -170,8 +173,14 @@ show_menu (grub_menu_t menu, int nested) /* Main event loop. */ int exit_requested = 0; + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_draw (view); + while ((! exit_requested) && (! grub_menu_viewer_should_return ())) { + grub_gfxmenu_redraw_timeout (view); if (grub_gfxmenu_model_timeout_expired (model)) { grub_gfxmenu_model_clear_timeout (model); @@ -181,9 +190,8 @@ show_menu (grub_menu_t menu, int nested) continue; } - grub_gfxmenu_view_draw (view); - grub_video_swap_buffers (); handle_key_events (model, view, nested, &exit_requested); + grub_cpu_idle (); } grub_gfxmenu_view_destroy (view); diff --git a/gfxmenu/gui_box.c b/gfxmenu/gui_box.c index 876d0733f..4fe131f98 100644 --- a/gfxmenu/gui_box.c +++ b/gfxmenu/gui_box.c @@ -166,7 +166,7 @@ layout_vertically (grub_gui_box_t self, int modify_layout, } static void -box_paint (void *vself) +box_paint (void *vself, const grub_video_rect_t *region) { grub_gui_box_t self = vself; struct component_node *cur; @@ -176,7 +176,7 @@ box_paint (void *vself) for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) { grub_gui_component_t comp = cur->component; - comp->ops->paint (comp); + comp->ops->paint (comp, region); } grub_gui_restore_viewport (&vpsave); } diff --git a/gfxmenu/gui_canvas.c b/gfxmenu/gui_canvas.c index a2cd77df6..d155364d7 100644 --- a/gfxmenu/gui_canvas.c +++ b/gfxmenu/gui_canvas.c @@ -79,7 +79,7 @@ canvas_is_instance (void *vself __attribute__((unused)), const char *type) } static void -canvas_paint (void *vself) +canvas_paint (void *vself, const grub_video_rect_t *region) { grub_gui_canvas_t self = vself; struct component_node *cur; @@ -106,7 +106,8 @@ canvas_paint (void *vself) } /* Paint the child. */ - comp->ops->paint (comp); + if (grub_video_have_common_points (region, &r)) + comp->ops->paint (comp, region); } grub_gui_restore_viewport (&vpsave); } diff --git a/gfxmenu/gui_circular_progress.c b/gfxmenu/gui_circular_progress.c index cd95c51f9..f3c8bc3af 100644 --- a/gfxmenu/gui_circular_progress.c +++ b/gfxmenu/gui_circular_progress.c @@ -112,12 +112,16 @@ check_pixmaps (circular_progress_t self) } static void -circprog_paint (void *vself) +circprog_paint (void *vself, const grub_video_rect_t *region) { circular_progress_t self = vself; if (! self->visible) return; + + if (!grub_video_have_common_points (region, &self->bounds)) + return; + if (! check_pixmaps (self)) return; diff --git a/gfxmenu/gui_image.c b/gfxmenu/gui_image.c index 1c58a535b..eb2ff1ee0 100644 --- a/gfxmenu/gui_image.c +++ b/gfxmenu/gui_image.c @@ -67,12 +67,16 @@ image_is_instance (void *vself __attribute__((unused)), const char *type) } static void -image_paint (void *vself) +image_paint (void *vself, const grub_video_rect_t *region) { grub_gui_image_t self = vself; + grub_video_rect_t vpsave; + if (! self->bitmap) return; - grub_video_rect_t vpsave; + if (!grub_video_have_common_points (region, &self->bounds)) + return; + grub_gui_set_viewport (&self->bounds, &vpsave); grub_video_blit_bitmap (self->bitmap, GRUB_VIDEO_BLIT_BLEND, 0, 0, 0, 0, diff --git a/gfxmenu/gui_label.c b/gfxmenu/gui_label.c index eb2a495ff..30474f52f 100644 --- a/gfxmenu/gui_label.c +++ b/gfxmenu/gui_label.c @@ -77,13 +77,16 @@ label_is_instance (void *vself __attribute__((unused)), const char *type) } static void -label_paint (void *vself) +label_paint (void *vself, const grub_video_rect_t *region) { grub_gui_label_t self = vself; if (! self->visible) return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; + /* Calculate the starting x coordinate. */ int left_x; if (self->align == align_left) diff --git a/gfxmenu/gui_list.c b/gfxmenu/gui_list.c index aa9655b45..bf6d94657 100644 --- a/gfxmenu/gui_list.c +++ b/gfxmenu/gui_list.c @@ -311,16 +311,18 @@ draw_menu (list_impl_t self) } static void -list_paint (void *vself) +list_paint (void *vself, const grub_video_rect_t *region) { list_impl_t self = vself; + grub_video_rect_t vpsave; if (! self->visible) return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; check_boxes (self); - grub_video_rect_t vpsave; grub_gui_set_viewport (&self->bounds, &vpsave); draw_menu (self); grub_gui_restore_viewport (&vpsave); diff --git a/gfxmenu/gui_progress_bar.c b/gfxmenu/gui_progress_bar.c index 440d4b2fc..498711169 100644 --- a/gfxmenu/gui_progress_bar.c +++ b/gfxmenu/gui_progress_bar.c @@ -171,11 +171,13 @@ draw_text (grub_gui_progress_bar_t self) } static void -progress_bar_paint (void *vself) +progress_bar_paint (void *vself, const grub_video_rect_t *region) { grub_gui_progress_bar_t self = vself; if (! self->visible) return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; grub_video_rect_t vpsave; grub_gui_set_viewport (&self->bounds, &vpsave); diff --git a/gfxmenu/view.c b/gfxmenu/view.c index e807d3838..1c512fee1 100644 --- a/gfxmenu/view.c +++ b/gfxmenu/view.c @@ -55,6 +55,8 @@ grub_gfxmenu_view_t grub_gfxmenu_view_new (const char *theme_path, grub_gfxmenu_model_t model) { grub_gfxmenu_view_t view; + grub_err_t err; + struct grub_video_mode_info mode_info; view = grub_malloc (sizeof (*view)); if (! view) @@ -67,6 +69,18 @@ grub_gfxmenu_view_new (const char *theme_path, grub_gfxmenu_model_t model) (unsigned *) &view->screen.width, (unsigned *) &view->screen.height); + err = grub_video_get_info (&mode_info); + if (err) + { + grub_free (view); + return 0; + } + else + view->double_repaint = (mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) + && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + /* Clear the screen; there may be garbage left over in video memory, and loading the menu style (particularly the background) can take a while. */ grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), @@ -97,6 +111,15 @@ grub_gfxmenu_view_new (const char *theme_path, grub_gfxmenu_model_t model) view->title_text = grub_strdup ("GRUB Boot Menu"); view->progress_message_text = 0; view->theme_path = 0; + view->last_seconds_remaining = -2; + + /* Set the timeout bar's frame. */ + view->progress_message_frame.width = view->screen.width * 4 / 5; + view->progress_message_frame.height = 50; + view->progress_message_frame.x = view->screen.x + + (view->screen.width - view->progress_message_frame.width) / 2; + view->progress_message_frame.y = view->screen.y + + view->screen.height - 90 - 20 - view->progress_message_frame.height; if (grub_gfxmenu_view_load_theme (view, theme_path) != 0) { @@ -141,21 +164,23 @@ set_progress_message (grub_gfxmenu_view_t view, const char *message) } static void -draw_background (grub_gfxmenu_view_t view) +redraw_background (grub_gfxmenu_view_t view, + const grub_video_rect_t *bounds) { if (view->desktop_image) { struct grub_video_bitmap *img = view->desktop_image; grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, - view->screen.x, view->screen.y, 0, 0, - grub_video_bitmap_get_width (img), - grub_video_bitmap_get_height (img)); + bounds->x, bounds->y, + bounds->x - view->screen.x, + bounds->y - view->screen.y, + bounds->width, bounds->height); } else { grub_video_fill_rect (grub_gui_map_color (view->desktop_color), - view->screen.x, view->screen.y, - view->screen.width, view->screen.height); + bounds->x, bounds->y, + bounds->width, bounds->height); } } @@ -191,6 +216,7 @@ update_timeout_visit (grub_gui_component_t component, { struct progress_value_data *pv; pv = (struct progress_value_data *) userdata; + component->ops->set_property (component, "visible", pv->visible); component->ops->set_property (component, "start", pv->start); component->ops->set_property (component, "end", pv->end); @@ -198,16 +224,43 @@ update_timeout_visit (grub_gui_component_t component, component->ops->set_property (component, "text", pv->text); } -static void -update_timeout (grub_gfxmenu_view_t view) + +static inline void +update_timeout (grub_gfxmenu_view_t view, int is_init) { char startbuf[20]; char valuebuf[20]; char msgbuf[120]; - int timeout = grub_gfxmenu_model_get_timeout_ms (view->model); - int remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model); + int timeout; + int remaining; struct progress_value_data pv; + int seconds_remaining_rounded_up; + + auto void redraw_timeout_visit (grub_gui_component_t component, + void *userdata __attribute__ ((unused))); + + auto void redraw_timeout_visit (grub_gui_component_t component, + void *userdata __attribute__ ((unused))) + { + grub_video_rect_t bounds; + component->ops->get_bounds (component, &bounds); + grub_gfxmenu_view_redraw (view, &bounds); + } + + timeout = grub_gfxmenu_model_get_timeout_ms (view->model); + if (timeout > 0) + { + remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model); + seconds_remaining_rounded_up = (remaining + 999) / 1000; + } + else + seconds_remaining_rounded_up = -1; + + if (view->last_seconds_remaining == seconds_remaining_rounded_up && !is_init) + return; + + view->last_seconds_remaining = seconds_remaining_rounded_up; pv.visible = timeout > 0 ? "true" : "false"; grub_sprintf (startbuf, "%d", -timeout); @@ -216,7 +269,6 @@ update_timeout (grub_gfxmenu_view_t view) grub_sprintf (valuebuf, "%d", remaining > 0 ? -remaining : 0); pv.value = valuebuf; - int seconds_remaining_rounded_up = (remaining + 999) / 1000; grub_sprintf (msgbuf, "The highlighted entry will be booted automatically in %d s.", seconds_remaining_rounded_up); @@ -224,6 +276,21 @@ update_timeout (grub_gfxmenu_view_t view) grub_gui_find_by_id ((grub_gui_component_t) view->canvas, TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); + if (!is_init) + { + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + } +} + +void +grub_gfxmenu_redraw_timeout (grub_gfxmenu_view_t view) +{ + update_timeout (view, 0); } static void @@ -252,19 +319,13 @@ static void draw_message (grub_gfxmenu_view_t view) { char *text = view->progress_message_text; + grub_video_rect_t f = view->progress_message_frame; if (! text) return; grub_font_t font = view->message_font; grub_video_color_t color = grub_gui_map_color (view->message_color); - /* Set the timeout bar's frame. */ - grub_video_rect_t f; - f.width = view->screen.width * 4 / 5; - f.height = 50; - f.x = view->screen.x + (view->screen.width - f.width) / 2; - f.y = view->screen.y + view->screen.height - 90 - 20 - f.height; - /* Border. */ grub_video_fill_rect (color, f.x-1, f.y-1, f.width+2, f.height+2); @@ -280,19 +341,62 @@ draw_message (grub_gfxmenu_view_t view) grub_font_draw_string (text, font, color, x, y); } - void -grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) +grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, + const grub_video_rect_t *region) { grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - update_timeout (view); - update_menu_components (view); - draw_background (view); + redraw_background (view, region); if (view->canvas) - view->canvas->ops->component.paint (view->canvas); + view->canvas->ops->component.paint (view->canvas, region); draw_title (view); - draw_message (view); + if (grub_video_have_common_points (&view->progress_message_frame, region)) + draw_message (view); +} + +void +grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) +{ + update_timeout (view, 1); + update_menu_components (view); + + grub_gfxmenu_view_redraw (view, &view->screen); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->screen); +} + +static void +redraw_menu_visit (grub_gui_component_t component, + void *userdata) +{ + grub_gfxmenu_view_t view; + view = userdata; + if (component->ops->is_instance (component, "list")) + { + grub_gui_list_t list; + grub_video_rect_t bounds; + + list = (grub_gui_list_t) component; + component->ops->get_bounds (component, &bounds); + grub_gfxmenu_view_redraw (view, &bounds); + } +} + +void +grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view) +{ + update_menu_components (view); + + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + redraw_menu_visit, view); + grub_video_swap_buffers (); + if (view->double_repaint) + { + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + redraw_menu_visit, view); + } } static grub_err_t @@ -316,7 +420,7 @@ static int term_initialized; static grub_term_output_t term_original; static void -draw_terminal_box (void) +draw_terminal_box (grub_gfxmenu_view_t view) { grub_gfxmenu_box_t term_box; int termx; @@ -335,6 +439,11 @@ draw_terminal_box (void) term_box->draw (term_box, termx - term_box->get_left_pad (term_box), termy - term_box->get_top_pad (term_box)); + grub_video_swap_buffers (); + if (view->double_repaint) + term_box->draw (term_box, + termx - term_box->get_left_pad (term_box), + termy - term_box->get_top_pad (term_box)); } static void @@ -342,9 +451,6 @@ init_terminal (grub_gfxmenu_view_t view) { int termx; int termy; - struct grub_video_mode_info mode_info; - grub_err_t err; - int double_repaint; term_original = grub_term_get_current_output (); @@ -354,34 +460,20 @@ init_terminal (grub_gfxmenu_view_t view) termx = view->screen.x + view->screen.width * (10 - 7) / 10 / 2; termy = view->screen.y + view->screen.height * (10 - 7) / 10 / 2; - err = grub_video_get_info (&mode_info); - if (err) - { - grub_errno = GRUB_ERR_NONE; - double_repaint = 1; - } - else - double_repaint = (mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) - && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); - /* Note: currently there is no API for changing the gfxterm font on the fly, so whatever font the initially loaded theme specifies will be permanent. */ grub_gfxterm_init_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, termx, termy, term_target_width, term_target_height, - double_repaint, view->terminal_font_name, 3); + view->double_repaint, view->terminal_font_name, 3); if (grub_errno != GRUB_ERR_NONE) return; term_initialized = 1; - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - grub_gfxmenu_view_draw (view); - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - term_view = view; grub_term_set_current_output (grub_gfxterm_get_term ()); + grub_refresh (); } static void destroy_terminal (void) @@ -405,8 +497,10 @@ notify_booting (grub_menu_entry_t entry, void *userdata) grub_sprintf (s, "Booting '%s'", entry->title); set_progress_message (view, s); grub_free (s); - grub_gfxmenu_view_draw (view); + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); } static void @@ -421,8 +515,10 @@ notify_fallback (grub_menu_entry_t entry, void *userdata) grub_sprintf (s, "Falling back to '%s'", entry->title); set_progress_message (view, s); grub_free (s); - grub_gfxmenu_view_draw (view); + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); } static void @@ -442,6 +538,8 @@ int grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, grub_menu_entry_t entry) { + draw_terminal_box (view); + grub_menu_execute_with_fallback (grub_gfxmenu_model_get_menu (view->model), entry, &execute_callback, (void *) view); @@ -453,7 +551,6 @@ grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, "Unable to automatically boot. " "Press SPACE to continue."); grub_gfxmenu_view_draw (view); - grub_video_swap_buffers (); while (GRUB_TERM_ASCII_CHAR(grub_getkey ()) != ' ') { /* Wait for SPACE to be pressed. */ @@ -461,6 +558,11 @@ grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, set_progress_message (view, 0); /* Clear the message. */ + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); + return 1; /* Ok. */ } @@ -468,14 +570,7 @@ int grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, grub_menu_entry_t entry) { - /* Currently we switch back to text mode by restoring - the original terminal before executing the menu entry. - It is hard to make it work when executing a menu entry - that switches video modes -- it using gfxterm in a - window, the repaint callback seems to crash GRUB. */ - /* TODO: Determine if this works when 'gfxterm' was set as - the current terminal before invoking the gfxmenu. */ - destroy_terminal (); + draw_terminal_box (view); grub_menu_execute_entry (entry); if (grub_errno != GRUB_ERR_NONE) @@ -484,13 +579,14 @@ grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, if (set_graphics_mode () != GRUB_ERR_NONE) return 0; /* Failure. */ - init_terminal (view); + grub_gfxmenu_view_draw (view); return 1; /* Ok. */ } void -grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view __attribute__((unused))) +grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view) { - draw_terminal_box (); + draw_terminal_box (view); grub_cmdline_run (1); + grub_gfxmenu_view_draw (view); } diff --git a/include/grub/gfxmenu_view.h b/include/grub/gfxmenu_view.h index 1144382f2..c86334400 100644 --- a/include/grub/gfxmenu_view.h +++ b/include/grub/gfxmenu_view.h @@ -54,7 +54,15 @@ int grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, void grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view); +void +grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view); +void +grub_gfxmenu_redraw_timeout (grub_gfxmenu_view_t view); + +void +grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, + const grub_video_rect_t *region); /* Implementation details -- this should not be used outside of the view itself. */ @@ -86,6 +94,12 @@ struct grub_gfxmenu_view grub_gui_container_t canvas; grub_gfxmenu_model_t model; + + int last_seconds_remaining; + + int double_repaint; + + grub_video_rect_t progress_message_frame; }; #endif /* ! GRUB_GFXMENU_VIEW_HEADER */ diff --git a/include/grub/gui.h b/include/grub/gui.h index 5b785dbb5..385c0962b 100644 --- a/include/grub/gui.h +++ b/include/grub/gui.h @@ -50,13 +50,14 @@ struct grub_gui_component_ops void (*destroy) (void *self); const char * (*get_id) (void *self); int (*is_instance) (void *self, const char *type); - void (*paint) (void *self); + void (*paint) (void *self, const grub_video_rect_t *bounds); void (*set_parent) (void *self, grub_gui_container_t parent); grub_gui_container_t (*get_parent) (void *self); void (*set_bounds) (void *self, const grub_video_rect_t *bounds); void (*get_bounds) (void *self, grub_video_rect_t *bounds); void (*get_preferred_size) (void *self, int *width, int *height); grub_err_t (*set_property) (void *self, const char *name, const char *value); + void (*repaint) (void *self, int second_pass); }; struct grub_gui_container_ops @@ -162,4 +163,17 @@ grub_gui_map_color (grub_gui_color_t c) return grub_video_map_rgba (c.red, c.green, c.blue, c.alpha); } +static inline int +grub_video_have_common_points (const grub_video_rect_t *a, + const grub_video_rect_t *b) +{ + if (!((a->x <= b->x && b->x <= a->x + a->width) + || (b->x <= a->x && a->x <= b->x + b->width))) + return 0; + if (!((a->y <= b->y && b->y <= a->y + a->height) + || (b->y <= a->y && a->y <= b->y + b->height))) + return 0; + return 1; +} + #endif /* ! GRUB_GUI_H */ -- 2.39.5