diff --git a/include/grub/video_fb.h b/include/grub/video_fb.h index 3046a597b..2c1907fdf 100644 --- a/include/grub/video_fb.h +++ b/include/grub/video_fb.h @@ -119,11 +119,18 @@ 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 -grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **front, - struct grub_video_fbrender_target **back, - grub_video_fb_doublebuf_update_screen_t *update_screen, - struct grub_video_mode_info mode_info, - void *framebuf); +grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, + struct grub_video_mode_info *mode_info, + volatile void *page0_ptr, + grub_video_fb_set_page_t set_page_in, + volatile void *page1_ptr); +grub_err_t +grub_video_fb_swap_buffers (void); +grub_err_t +grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf); #endif /* ! GRUB_VIDEO_FB_HEADER */ diff --git a/video/fb/video_fb.c b/video/fb/video_fb.c index 9c5577bb9..6ec61868d 100644 --- a/video/fb/video_fb.c +++ b/video/fb/video_fb.c @@ -26,8 +26,16 @@ #include <grub/bitmap.h> static struct grub_video_fbrender_target *render_target; +static struct grub_video_fbrender_target *front_target; +static struct grub_video_fbrender_target *back_target; struct grub_video_palette_data *palette; static unsigned int palette_size; +/* For page flipping strategy. */ +static int displayed_page; /* The page # that is the front buffer. */ +static int render_page; /* The page # that is the back buffer. */ +static grub_video_fb_set_page_t set_page; +static char *offscreen_buffer; +static grub_video_fb_doublebuf_update_screen_t update_screen; /* Specify "standard" VGA palette, some video cards may need this and this will also be used when using RGB modes. */ @@ -58,8 +66,11 @@ grub_video_fb_init (void) { grub_free (palette); render_target = 0; + front_target = 0; + back_target = 0; palette = 0; palette_size = 0; + set_page = 0; return GRUB_ERR_NONE; } @@ -68,10 +79,15 @@ grub_video_fb_fini (void) { /* TODO: destroy render targets. */ + grub_free (offscreen_buffer); grub_free (palette); render_target = 0; + front_target = 0; + back_target = 0; palette = 0; palette_size = 0; + set_page = 0; + offscreen_buffer = 0; return GRUB_ERR_NONE; } @@ -1221,6 +1237,10 @@ grub_video_fb_delete_render_target (struct grub_video_fbrender_target *target) grub_err_t grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target) { + if (target == (struct grub_video_fbrender_target *) + GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = back_target; + if (! target->data) return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid render target given"); @@ -1235,6 +1255,9 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ { *target = render_target; + if (*target == back_target) + *target = (struct grub_video_fbrender_target *) GRUB_VIDEO_RENDER_TARGET_DISPLAY; + return GRUB_ERR_NONE; } @@ -1247,16 +1270,14 @@ doublebuf_blit_update_screen (struct grub_video_fbrender_target *front, return GRUB_ERR_NONE; } -grub_err_t +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_update_screen_t *update_screen, struct grub_video_mode_info mode_info, void *framebuf) { grub_err_t err; int page_size = mode_info.pitch * mode_info.height; - void *offscreen_buffer; err = grub_video_fb_create_render_target_from_pointer (front, &mode_info, framebuf); @@ -1283,7 +1304,200 @@ grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **front, } (*back)->is_allocated = 1; - *update_screen = doublebuf_blit_update_screen; + update_screen = doublebuf_blit_update_screen; + + 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))) +{ + int new_displayed_page; + struct grub_video_fbrender_target *target; + grub_err_t err; + + /* Swap the page numbers in the framebuffer struct. */ + new_displayed_page = render_page; + render_page = displayed_page; + displayed_page = new_displayed_page; + + err = set_page (displayed_page); + if (err) + { + /* Restore previous state. */ + render_page = displayed_page; + displayed_page = new_displayed_page; + return err; + } + + target = back_target; + back_target = front_target; + front_target = target; + + if (front_target->mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP) + grub_memcpy (back_target->data, front_target->data, + back_target->mode_info.pitch * back_target->mode_info.height); + + err = grub_video_fb_get_active_render_target (&target); + if (err) + return err; + + if (render_target == back_target) + render_target = front_target; + else if (target == front_target) + render_target = back_target; + + return err; +} + +static grub_err_t +doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info, + volatile void *page0_ptr, + grub_video_fb_set_page_t set_page_in, + volatile void *page1_ptr) +{ + grub_err_t err; + + displayed_page = 0; + render_page = 1; + + update_screen = doublebuf_pageflipping_update_screen; + + err = grub_video_fb_create_render_target_from_pointer (&front_target, + mode_info, + (void *) page0_ptr); + if (err) + return err; + + err = grub_video_fb_create_render_target_from_pointer (&back_target, + mode_info, + (void *) page1_ptr); + if (err) + { + grub_video_fb_delete_render_target (front_target); + return err; + } + + /* Set the framebuffer memory data pointer and display the right page. */ + err = set_page_in (displayed_page); + if (err) + { + grub_video_fb_delete_render_target (front_target); + grub_video_fb_delete_render_target (back_target); + return err; + } + set_page = set_page_in; + + return GRUB_ERR_NONE; +} + +/* Select the best double buffering mode available. */ +grub_err_t +grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, + struct grub_video_mode_info *mode_info, + volatile void *page0_ptr, + grub_video_fb_set_page_t set_page_in, + 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)) + { + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (updating_swap_needed) + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; + + err = doublebuf_pageflipping_init (mode_info, page0_ptr, + set_page_in, + page1_ptr); + if (!err) + { + render_target = back_target; + return GRUB_ERR_NONE; + } + + mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + grub_errno = GRUB_ERR_NONE; + } + + if (grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, + 0)) + { + mode_info->mode_type |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + err = grub_video_fb_doublebuf_blit_init (&front_target, + &back_target, + *mode_info, + (void *) page0_ptr); + + if (!err) + { + render_target = back_target; + return GRUB_ERR_NONE; + } + + mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + grub_errno = GRUB_ERR_NONE; + } + + /* Fall back to no double buffering. */ + err = grub_video_fb_create_render_target_from_pointer (&front_target, + mode_info, + (void *) page0_ptr); + + if (err) + return err; + + back_target = front_target; + update_screen = 0; + + mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + + render_target = back_target; + + return GRUB_ERR_NONE; +} + + +grub_err_t +grub_video_fb_swap_buffers (void) +{ + grub_err_t err; + if (!update_screen) + return GRUB_ERR_NONE; + + err = update_screen (front_target, back_target); + if (err) + return err; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(front_target->mode_info), sizeof (*mode_info)); + *framebuf = front_target->data; + + grub_video_fb_fini (); return GRUB_ERR_NONE; } diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c index 72b8f1831..05c6db736 100644 --- a/video/i386/pc/vbe.c +++ b/video/i386/pc/vbe.c @@ -40,25 +40,12 @@ static grub_uint32_t last_set_mode = 3; static struct { struct grub_video_mode_info mode_info; - struct grub_video_render_target *front_target; - struct grub_video_render_target *back_target; unsigned int bytes_per_scan_line; unsigned int bytes_per_pixel; grub_uint32_t active_vbe_mode; grub_uint8_t *ptr; int index_color_mode; - - char *offscreen_buffer; - - grub_size_t page_size; /* The size of a page in bytes. */ - - /* For page flipping strategy. */ - int displayed_page; /* The page # that is the front buffer. */ - int render_page; /* The page # that is the back buffer. */ - - /* Virtual functions. */ - grub_video_fb_doublebuf_update_screen_t update_screen; } framebuffer; static grub_uint32_t initial_vbe_mode; @@ -377,7 +364,6 @@ grub_video_vbe_fini (void) vbe_mode_list = NULL; err = grub_video_fb_fini (); - grub_free (framebuffer.offscreen_buffer); return err; } @@ -387,11 +373,11 @@ grub_video_vbe_fini (void) respectively. */ static grub_err_t -doublebuf_pageflipping_commit (void) +doublebuf_pageflipping_set_page (int page) { /* Tell the video adapter to display the new front page. */ int display_start_line - = framebuffer.mode_info.height * framebuffer.displayed_page; + = framebuffer.mode_info.height * page; grub_vbe_status_t vbe_err = grub_vbe_bios_set_display_start (0, display_start_line); @@ -402,164 +388,6 @@ doublebuf_pageflipping_commit (void) return 0; } -static grub_err_t -doublebuf_pageflipping_update_screen (struct grub_video_fbrender_target *front - __attribute__ ((unused)), - struct grub_video_fbrender_target *back - __attribute__ ((unused))) -{ - int new_displayed_page; - struct grub_video_fbrender_target *target; - grub_err_t err; - - /* Swap the page numbers in the framebuffer struct. */ - new_displayed_page = framebuffer.render_page; - framebuffer.render_page = framebuffer.displayed_page; - framebuffer.displayed_page = new_displayed_page; - - err = doublebuf_pageflipping_commit (); - if (err) - { - /* Restore previous state. */ - framebuffer.render_page = framebuffer.displayed_page; - framebuffer.displayed_page = new_displayed_page; - return err; - } - - if (framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP) - grub_memcpy (framebuffer.ptr + framebuffer.render_page - * framebuffer.page_size, framebuffer.ptr - + framebuffer.displayed_page * framebuffer.page_size, - framebuffer.page_size); - - target = framebuffer.back_target; - framebuffer.back_target = framebuffer.front_target; - framebuffer.front_target = target; - - err = grub_video_fb_get_active_render_target (&target); - if (err) - return err; - - if (target == framebuffer.back_target) - err = grub_video_fb_set_active_render_target (framebuffer.front_target); - else if (target == framebuffer.front_target) - err = grub_video_fb_set_active_render_target (framebuffer.back_target); - - return err; -} - -static grub_err_t -doublebuf_pageflipping_init (void) -{ - /* Get video RAM size in bytes. */ - grub_size_t vram_size = controller_info.total_memory << 16; - grub_err_t err; - - framebuffer.page_size = - framebuffer.mode_info.pitch * framebuffer.mode_info.height; - - if (2 * framebuffer.page_size > vram_size) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "Not enough video memory for double buffering."); - - 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, &framebuffer.mode_info, framebuffer.ptr); - if (err) - return err; - - err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, &framebuffer.mode_info, framebuffer.ptr + framebuffer.page_size); - if (err) - { - grub_video_fb_delete_render_target (framebuffer.front_target); - return err; - } - - /* Set the framebuffer memory data pointer and display the right page. */ - err = doublebuf_pageflipping_commit (); - if (err) - { - grub_video_fb_delete_render_target (framebuffer.front_target); - grub_video_fb_delete_render_target (framebuffer.back_target); - return err; - } - - return GRUB_ERR_NONE; -} - -/* Select the best double buffering mode available. */ -static grub_err_t -double_buffering_init (unsigned int mode_type, unsigned int mode_mask) -{ - 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 (grub_video_check_mode_flag (mode_type, mode_mask, - GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, - !updating_swap_needed)) - { - framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; - if (updating_swap_needed) - framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; - err = doublebuf_pageflipping_init (); - if (!err) - return GRUB_ERR_NONE; - - framebuffer.mode_info.mode_type - &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED - | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); - - grub_errno = GRUB_ERR_NONE; - } - - if (grub_video_check_mode_flag (mode_type, mode_mask, - GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, - 0)) - { - framebuffer.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, - &framebuffer.update_screen, - framebuffer.mode_info, - framebuffer.ptr); - - if (!err) - return GRUB_ERR_NONE; - - framebuffer.mode_info.mode_type - &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED - | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); - - grub_errno = GRUB_ERR_NONE; - } - - /* Fall back to no double buffering. */ - err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, &framebuffer.mode_info, framebuffer.ptr); - - if (err) - return err; - - framebuffer.back_target = framebuffer.front_target; - framebuffer.update_screen = 0; - - framebuffer.mode_info.mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; - - return GRUB_ERR_NONE; -} - - - static grub_err_t grub_video_vbe_setup (unsigned int width, unsigned int height, unsigned int mode_type, unsigned int mode_mask) @@ -684,15 +512,24 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); - /* Set up double buffering and targets. */ - err = double_buffering_init (mode_type, mode_mask); - if (err) - return err; + { + /* Get video RAM size in bytes. */ + grub_size_t vram_size = controller_info.total_memory << 16; + grub_size_t page_size; /* The size of a page in bytes. */ - err = grub_video_fb_set_active_render_target (framebuffer.back_target); + page_size = framebuffer.mode_info.pitch * framebuffer.mode_info.height; - if (err) - return err; + if (vram_size >= 2 * page_size) + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, + doublebuf_pageflipping_set_page, + framebuffer.ptr + page_size); + else + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, 0, 0); + } /* Copy default palette to initialize emulated palette. */ err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, @@ -723,59 +560,13 @@ grub_video_vbe_set_palette (unsigned int start, unsigned int count, return grub_video_fb_set_palette (start, count, palette_data); } -static grub_err_t -grub_video_vbe_swap_buffers (void) -{ - grub_err_t err; - if (!framebuffer.update_screen) - return GRUB_ERR_NONE; - - err = framebuffer.update_screen (framebuffer.front_target, - framebuffer.back_target); - if (err) - return err; - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_video_vbe_set_active_render_target (struct grub_video_render_target *target) -{ - if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) - target = framebuffer.back_target; - - return grub_video_fb_set_active_render_target (target); -} - -static grub_err_t -grub_video_vbe_get_active_render_target (struct grub_video_render_target **target) -{ - grub_err_t err; - err = grub_video_fb_get_active_render_target (target); - if (err) - return err; - - if (*target == framebuffer.back_target) - *target = GRUB_VIDEO_RENDER_TARGET_DISPLAY; - - return GRUB_ERR_NONE; -} - static grub_err_t grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info, void **framebuf) { - grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); - *framebuf = (char *) framebuffer.ptr - + framebuffer.displayed_page * framebuffer.page_size; - grub_free (vbe_mode_list); vbe_mode_list = NULL; - - grub_video_fb_fini (); - grub_free (framebuffer.offscreen_buffer); - - return GRUB_ERR_NONE; + return grub_video_fb_get_info_and_fini (mode_info, framebuf); } static struct grub_video_adapter grub_video_vbe_adapter = @@ -800,11 +591,11 @@ static struct grub_video_adapter grub_video_vbe_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_vbe_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_vbe_set_active_render_target, - .get_active_render_target = grub_video_vbe_get_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 };