diff --git a/ChangeLog b/ChangeLog index 96696791a..9167a3754 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2012-06-15 Vladimir Serbinenko + + Avoid slow read-back from VRAM. + + * include/grub/video_fb.h (grub_video_fb_doublebuf_update_screen_t): + Move from here ... + * grub-core/video/fb/video_fb.c + (grub_video_fb_doublebuf_update_screen_t): ... here. Remove arguments. + * grub-core/video/fb/video_fb.c (framebuf_t): New type. + (front_target): Remove front_target. Add pages. + (grub_video_fb_init): Skip setting front_pages. + (grub_video_fb_fini): Likewise. + (doublebuf_blit_update_screen): Use pages. + (grub_video_fb_doublebuf_blit_init): Likewise. + (doublebuf_pageflipping_init): Allocate offscreen buffer. + (doublebuf_pageflipping_update_screen): Use offscreen buffer. + (grub_video_fb_setup): Prefer doublebuffing. + 2012-06-15 Vladimir Serbinenko * grub-core/normal/main.c (GRUB_MOD_INIT): Ignore errors when loading diff --git a/grub-core/video/fb/video_fb.c b/grub-core/video/fb/video_fb.c index 2ab403db2..9d79f77b5 100644 --- a/grub-core/video/fb/video_fb.c +++ b/grub-core/video/fb/video_fb.c @@ -28,12 +28,15 @@ GRUB_MOD_LICENSE ("GPLv3+"); +typedef grub_err_t (*grub_video_fb_doublebuf_update_screen_t) (void); +typedef volatile void *framebuf_t; + static struct { struct grub_video_fbrender_target *render_target; - struct grub_video_fbrender_target *front_target; struct grub_video_fbrender_target *back_target; struct grub_video_palette_data *palette; + framebuf_t pages[2]; unsigned int palette_size; /* For page flipping strategy. */ int displayed_page; /* The page # that is the front buffer. */ @@ -72,7 +75,6 @@ grub_video_fb_init (void) { grub_free (framebuffer.palette); framebuffer.render_target = 0; - framebuffer.front_target = 0; framebuffer.back_target = 0; framebuffer.palette = 0; framebuffer.palette_size = 0; @@ -88,7 +90,6 @@ grub_video_fb_fini (void) grub_free (framebuffer.offscreen_buffer); grub_free (framebuffer.palette); framebuffer.render_target = 0; - framebuffer.front_target = 0; framebuffer.back_target = 0; framebuffer.palette = 0; framebuffer.palette_size = 0; @@ -1283,64 +1284,57 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ } static grub_err_t -doublebuf_blit_update_screen (struct grub_video_fbrender_target *front, - struct grub_video_fbrender_target *back) +doublebuf_blit_update_screen (void) { - grub_memcpy (front->data, back->data, - front->mode_info.pitch * front->mode_info.height); + grub_memcpy ((void *) framebuffer.pages[0], framebuffer.back_target->data, + framebuffer.back_target->mode_info.pitch + * framebuffer.back_target->mode_info.height); return GRUB_ERR_NONE; } static grub_err_t -grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **front, - struct grub_video_fbrender_target **back, +grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **back, struct grub_video_mode_info mode_info, - void *framebuf) + volatile void *framebuf) { grub_err_t err; - int page_size = mode_info.pitch * mode_info.height; - - err = grub_video_fb_create_render_target_from_pointer (front, &mode_info, - framebuf); - if (err) - return err; + grub_size_t page_size = mode_info.pitch * mode_info.height; framebuffer.offscreen_buffer = grub_malloc (page_size); if (! framebuffer.offscreen_buffer) - { - grub_video_fb_delete_render_target (*front); - *front = 0; - return grub_errno; - } + return grub_errno; - err = grub_video_fb_create_render_target_from_pointer (back, &mode_info, + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, + &mode_info, framebuffer.offscreen_buffer); if (err) { - grub_video_fb_delete_render_target (*front); grub_free (framebuffer.offscreen_buffer); framebuffer.offscreen_buffer = 0; - *front = 0; return grub_errno; } (*back)->is_allocated = 1; framebuffer.update_screen = doublebuf_blit_update_screen; + framebuffer.pages[0] = framebuf; + framebuffer.displayed_page = 0; + framebuffer.render_page = 0; return GRUB_ERR_NONE; } static grub_err_t -doublebuf_pageflipping_update_screen (struct grub_video_fbrender_target *front - __attribute__ ((unused)), - struct grub_video_fbrender_target *back - __attribute__ ((unused))) +doublebuf_pageflipping_update_screen (void) { int new_displayed_page; - struct grub_video_fbrender_target *target; grub_err_t err; + grub_memcpy ((void *) framebuffer.pages[framebuffer.render_page], + framebuffer.back_target->data, + framebuffer.back_target->mode_info.pitch + * framebuffer.back_target->mode_info.height); + /* Swap the page numbers in the framebuffer struct. */ new_displayed_page = framebuffer.render_page; framebuffer.render_page = framebuffer.displayed_page; @@ -1355,26 +1349,7 @@ doublebuf_pageflipping_update_screen (struct grub_video_fbrender_target *front return err; } - target = framebuffer.back_target; - framebuffer.back_target = framebuffer.front_target; - framebuffer.front_target = target; - - if (framebuffer.front_target->mode_info.mode_type - & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP) - grub_memcpy (framebuffer.back_target->data, framebuffer.front_target->data, - framebuffer.back_target->mode_info.pitch - * framebuffer.back_target->mode_info.height); - - err = grub_video_fb_get_active_render_target (&target); - if (err) - return err; - - if (framebuffer.render_target == framebuffer.back_target) - framebuffer.render_target = framebuffer.front_target; - else if (framebuffer.render_target == framebuffer.front_target) - framebuffer.render_target = framebuffer.back_target; - - return err; + return GRUB_ERR_NONE; } static grub_err_t @@ -1384,32 +1359,37 @@ doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info, volatile void *page1_ptr) { grub_err_t err; + grub_size_t page_size = mode_info->pitch * mode_info->height; + + framebuffer.offscreen_buffer = grub_malloc (page_size); + if (! framebuffer.offscreen_buffer) + { + return grub_errno; + } + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, + mode_info, + framebuffer.offscreen_buffer); + + if (err) + { + grub_free (framebuffer.offscreen_buffer); + framebuffer.offscreen_buffer = 0; + return grub_errno; + } + framebuffer.back_target->is_allocated = 1; framebuffer.displayed_page = 0; framebuffer.render_page = 1; framebuffer.update_screen = doublebuf_pageflipping_update_screen; - - err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, - mode_info, - (void *) page0_ptr); - if (err) - return err; - - err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, - mode_info, - (void *) page1_ptr); - if (err) - { - grub_video_fb_delete_render_target (framebuffer.front_target); - return err; - } + framebuffer.pages[0] = page0_ptr; + framebuffer.pages[1] = page1_ptr; /* Set the framebuffer memory data pointer and display the right page. */ err = set_page_in (framebuffer.displayed_page); if (err) { - grub_video_fb_delete_render_target (framebuffer.front_target); grub_video_fb_delete_render_target (framebuffer.back_target); return err; } @@ -1427,20 +1407,14 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, volatile void *page1_ptr) { grub_err_t err; - int updating_swap_needed; - - updating_swap_needed - = grub_video_check_mode_flag (mode_type, mode_mask, - GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP, 0); /* Do double buffering only if it's either requested or efficient. */ if (set_page_in && grub_video_check_mode_flag (mode_type, mode_mask, GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, - !updating_swap_needed)) + 1)) { mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; - if (updating_swap_needed) - mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; err = doublebuf_pageflipping_init (mode_info, page0_ptr, set_page_in, @@ -1459,7 +1433,7 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, if (grub_video_check_mode_flag (mode_type, mode_mask, GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, - 0)) + 1)) { /* It was much nicer with the cast directly at function call but some older gcc versions don't accept it properly.*/ @@ -1467,8 +1441,7 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, mode_info->mode_type |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); - err = grub_video_fb_doublebuf_blit_init (&framebuffer.front_target, - &framebuffer.back_target, + err = grub_video_fb_doublebuf_blit_init (&framebuffer.back_target, *mode_info, tmp); @@ -1485,15 +1458,18 @@ grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, } /* Fall back to no double buffering. */ - err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, mode_info, (void *) page0_ptr); if (err) return err; - framebuffer.back_target = framebuffer.front_target; framebuffer.update_screen = 0; + framebuffer.pages[0] = page0_ptr; + framebuffer.displayed_page = 0; + framebuffer.render_page = 0; + framebuffer.set_page = 0; mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; @@ -1510,8 +1486,7 @@ grub_video_fb_swap_buffers (void) if (!framebuffer.update_screen) return GRUB_ERR_NONE; - err = framebuffer.update_screen (framebuffer.front_target, - framebuffer.back_target); + err = framebuffer.update_screen (); if (err) return err; @@ -1522,23 +1497,17 @@ grub_err_t grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info, void **framebuf) { - grub_memcpy (mode_info, &(framebuffer.front_target->mode_info), + grub_memcpy (mode_info, &(framebuffer.back_target->mode_info), sizeof (*mode_info)); /* We are about to load a kernel. Switch back to page zero, since some kernel drivers expect that. */ - if ((mode_info->mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) - && framebuffer.set_page && framebuffer.displayed_page != 0) + if (framebuffer.set_page && framebuffer.displayed_page != 0) { - /* Ensure both pages are exactly in sync. */ - grub_memcpy (framebuffer.back_target->data, - framebuffer.front_target->data, - framebuffer.back_target->mode_info.pitch - * framebuffer.back_target->mode_info.height); - grub_video_swap_buffers (); + framebuffer.update_screen (); } - *framebuf = framebuffer.front_target->data; + *framebuf = (void *) framebuffer.pages[framebuffer.displayed_page]; grub_video_fb_fini (); diff --git a/grub-core/video/ieee1275.c b/grub-core/video/ieee1275.c index 913ea8376..84f9b89ac 100644 --- a/grub-core/video/ieee1275.c +++ b/grub-core/video/ieee1275.c @@ -40,7 +40,6 @@ static int have_setcolors = 0; static struct { struct grub_video_mode_info mode_info; - struct grub_video_render_target *render_target; grub_uint8_t *ptr; } framebuffer; @@ -186,23 +185,12 @@ grub_video_ieee1275_setup (unsigned int width, unsigned int height, grub_dprintf ("video", "IEEE1275: initialising FB @ %p %dx%dx%d\n", framebuffer.ptr, framebuffer.mode_info.width, framebuffer.mode_info.height, framebuffer.mode_info.bpp); - - err = grub_video_fb_create_render_target_from_pointer - (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr); + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, NULL, NULL); if (err) - { - grub_dprintf ("video", "IEEE1275: Couldn't create FB target\n"); - return err; - } - - err = grub_video_fb_set_active_render_target (framebuffer.render_target); - - if (err) - { - grub_dprintf ("video", "IEEE1275: Couldn't set FB target\n"); - return err; - } + return err; grub_video_ieee1275_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, grub_video_fbstd_colors); @@ -210,22 +198,6 @@ grub_video_ieee1275_setup (unsigned int width, unsigned int height, return err; } -static grub_err_t -grub_video_ieee1275_swap_buffers (void) -{ - /* TODO: Implement buffer swapping. */ - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_video_ieee1275_set_active_render_target (struct grub_video_render_target *target) -{ - if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) - target = framebuffer.render_target; - - return grub_video_fb_set_active_render_target (target); -} - static grub_err_t grub_video_ieee1275_get_info_and_fini (struct grub_video_mode_info *mode_info, void **framebuf) @@ -287,10 +259,10 @@ static struct grub_video_adapter grub_video_ieee1275_adapter = .blit_bitmap = grub_video_fb_blit_bitmap, .blit_render_target = grub_video_fb_blit_render_target, .scroll = grub_video_fb_scroll, - .swap_buffers = grub_video_ieee1275_swap_buffers, + .swap_buffers = grub_video_fb_swap_buffers, .create_render_target = grub_video_fb_create_render_target, .delete_render_target = grub_video_fb_delete_render_target, - .set_active_render_target = grub_video_ieee1275_set_active_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, .get_active_render_target = grub_video_fb_get_active_render_target, .next = 0 diff --git a/include/grub/video_fb.h b/include/grub/video_fb.h index 6816f6f60..d696398fe 100644 --- a/include/grub/video_fb.h +++ b/include/grub/video_fb.h @@ -115,10 +115,6 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ grub_err_t grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target); -typedef grub_err_t -(*grub_video_fb_doublebuf_update_screen_t) (struct grub_video_fbrender_target *front, - struct grub_video_fbrender_target *back); - typedef grub_err_t (*grub_video_fb_set_page_t) (int page); grub_err_t