#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. */
{
grub_free (palette);
render_target = 0;
+ front_target = 0;
+ back_target = 0;
palette = 0;
palette_size = 0;
+ set_page = 0;
return GRUB_ERR_NONE;
}
{
/* 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;
}
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");
{
*target = render_target;
+ if (*target == back_target)
+ *target = (struct grub_video_fbrender_target *) GRUB_VIDEO_RENDER_TARGET_DISPLAY;
+
return GRUB_ERR_NONE;
}
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);
}
(*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;
}
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;
vbe_mode_list = NULL;
err = grub_video_fb_fini ();
- grub_free (framebuffer.offscreen_buffer);
return err;
}
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);
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)
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;
-
- err = grub_video_fb_set_active_render_target (framebuffer.back_target);
-
- 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. */
+
+ page_size = framebuffer.mode_info.pitch * framebuffer.mode_info.height;
+
+ 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,
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 =
.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
};