-- 
Regards
Vladimir 'phcoder' Serbinenko

Personal git repository: http://repo.or.cz/w/grub2/phcoder.git
diff --git a/ChangeLog b/ChangeLog
index 5701f9b..6f03a92 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2009-08-24  Colin D Bennett  <co...@gibibit.com>
+2009-08-24  Vladimir Serbinenko  <phco...@gmail.com>
+
+       Double buffering support.
+
+       * commands/i386/pc/videotest.c (grub_cmd_videotest): Swap doublebuffers.
+       * include/grub/video.h: Update comment.
+       * include/grub/video_fb.h (grub_video_fb_doublebuf_update_screen_t):
+       New type.
+       (grub_video_fb_doublebuf_blit_init): New prototype.
+       * term/gfxterm.c (scroll_up): Support double buffering.
+       (grub_gfxterm_refresh): Likewise.
+       * video/fb/video_fb.c (doublebuf_blit_update_screen): New function.
+       (grub_video_fb_doublebuf_blit_init): Likewise.
+       * video/i386/pc/vbe.c (framebuffer): Remove 'render_target'. Add
+       'front_target', 'back_target', 'offscreen_buffer', 'page_size',
+       'displayed_page', 'render_page' and 'update_screen'.
+       (grub_video_vbe_fini): Free offscreen buffer.
+       (doublebuf_pageflipping_commit): New function.
+       (doublebuf_pageflipping_update_screen): Likewise.
+       (doublebuf_pageflipping_init): Likewise.
+       (double_buffering_init): Likewise.
+       (grub_video_vbe_setup): Enable doublebuffering.
+       (grub_video_vbe_swap_buffers): Implement.
+       (grub_video_vbe_set_active_render_target): Handle double buffering.
+       (grub_video_vbe_get_active_render_target): Likewise.
+       (grub_video_vbe_get_info_and_fini): Likewise. Free offscreen_buffer.
+       (grub_video_vbe_adapter): Use grub_video_vbe_get_active_render_target.
+       (grub_video_vbe_enable_double_buffering): Likewise.
+       (grub_video_vbe_swap_buffers): Use update_screen.
+       (grub_video_set_mode): Use double buffering.
+
 2009-08-23  Vladimir Serbinenko  <phco...@gmail.com>
 
        Fix grub-install.
diff --git a/commands/videotest.c b/commands/videotest.c
index 07735cd..efe3cff 100644
--- a/commands/videotest.c
+++ b/commands/videotest.c
@@ -160,6 +160,7 @@ grub_cmd_videotest (grub_command_t cmd __attribute__ 
((unused)),
       grub_video_fill_rect (color, 0, 0, width, height);
       grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, 0, 0,
                                      0, 0, width, height);
+      grub_video_swap_buffers ();
     }
 
   grub_getkey ();
diff --git a/include/grub/video.h b/include/grub/video.h
index 94b0467..0cea9f3 100644
--- a/include/grub/video.h
+++ b/include/grub/video.h
@@ -48,6 +48,8 @@ struct grub_video_bitmap;
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK                0x0000ff00
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS         8
 
+/* The basic render target representing the whole display.  This always
+   renders to the back buffer when double-buffering is in use.  */
 #define GRUB_VIDEO_RENDER_TARGET_DISPLAY \
   ((struct grub_video_render_target *) 0)
 
diff --git a/include/grub/video_fb.h b/include/grub/video_fb.h
index 17debd6..3046a59 100644
--- a/include/grub/video_fb.h
+++ b/include/grub/video_fb.h
@@ -115,4 +115,15 @@ 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);
+
+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);
+
 #endif /* ! GRUB_VIDEO_FB_HEADER */
diff --git a/term/gfxterm.c b/term/gfxterm.c
index 57c51cf..1ea9fda 100644
--- a/term/gfxterm.c
+++ b/term/gfxterm.c
@@ -608,6 +608,8 @@ scroll_up (void)
       if (virtual_screen.cursor_state)
        draw_cursor (1);
     }
+
+  grub_video_swap_buffers ();
 }
 
 static void
@@ -877,6 +879,8 @@ grub_gfxterm_refresh (void)
 {
   /* Redraw only changed regions.  */
   dirty_region_redraw ();
+
+  grub_video_swap_buffers ();
 }
 
 static grub_err_t
diff --git a/video/fb/video_fb.c b/video/fb/video_fb.c
index a35dd7a..d0680cc 100644
--- a/video/fb/video_fb.c
+++ b/video/fb/video_fb.c
@@ -66,6 +66,8 @@ grub_video_fb_init (void)
 grub_err_t
 grub_video_fb_fini (void)
 {
+  /* TODO: destroy render targets.  */
+
   grub_free (palette);
   render_target = 0;
   palette = 0;
@@ -774,9 +776,9 @@ grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap,
 
 grub_err_t
 grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source,
-                                   enum grub_video_blit_operators oper,
-                                   int x, int y, int offset_x, int offset_y,
-                                   unsigned int width, unsigned int height)
+                                  enum grub_video_blit_operators oper,
+                                  int x, int y, int offset_x, int offset_y,
+                                  unsigned int width, unsigned int height)
 {
   struct grub_video_fbblit_info source_info;
   struct grub_video_fbblit_info target_info;
@@ -1116,3 +1118,53 @@ grub_video_fb_get_active_render_target (struct 
grub_video_fbrender_target **targ
 
   return GRUB_ERR_NONE;
 }
+
+static grub_err_t
+doublebuf_blit_update_screen (struct grub_video_fbrender_target *front,
+                             struct grub_video_fbrender_target *back)
+{
+  grub_memcpy (front->data, back->data,
+              front->mode_info.pitch * front->mode_info.height);
+  return GRUB_ERR_NONE;
+}
+
+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);
+  if (err)
+    return err;
+
+  offscreen_buffer = grub_malloc (page_size);
+  if (! offscreen_buffer)
+    {
+      grub_video_fb_delete_render_target (*front);
+      *front = 0;
+      return grub_errno;
+    }
+
+  err = grub_video_fb_create_render_target_from_pointer (back, &mode_info,
+                                                        offscreen_buffer);
+
+  if (err)
+    {
+      grub_video_fb_delete_render_target (*front);
+      grub_free (offscreen_buffer);
+      *front = 0;
+      return grub_errno;
+    }
+  (*back)->is_allocated = 1;
+
+  *update_screen = doublebuf_blit_update_screen;
+
+  return GRUB_ERR_NONE;
+}
diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c
index b8f83bc..ee38222 100644
--- a/video/i386/pc/vbe.c
+++ b/video/i386/pc/vbe.c
@@ -36,13 +36,25 @@ static struct grub_vbe_mode_info_block active_vbe_mode_info;
 static struct
 {
   struct grub_video_mode_info mode_info;
-  struct grub_video_render_target *render_target;
+  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;
@@ -362,6 +374,7 @@ static grub_err_t
 grub_video_vbe_fini (void)
 {
   grub_vbe_status_t status;
+  grub_err_t err;
 
   /* Restore old video mode.  */
   status = grub_vbe_bios_set_mode (initial_vbe_mode, 0);
@@ -373,12 +386,165 @@ grub_video_vbe_fini (void)
   grub_free (vbe_mode_list);
   vbe_mode_list = NULL;
 
-  /* TODO: destroy render targets.  */
+  err = grub_video_fb_fini ();
+  grub_free (framebuffer.offscreen_buffer);
+  return err;
+}
+
+/*
+   Set framebuffer render target page and display the proper page, based on
+   `doublebuf_state.render_page' and `doublebuf_state.displayed_page',
+   respectively.
+ */
+static grub_err_t
+doublebuf_pageflipping_commit (void)
+{
+  /* Tell the video adapter to display the new front page.  */
+  int display_start_line =
+    framebuffer.mode_info.height
+    * framebuffer.displayed_page;
+
+  grub_vbe_status_t vbe_err =
+    grub_vbe_bios_set_display_start (0, display_start_line);
+
+  if (vbe_err != GRUB_VBE_STATUS_OK)
+    return grub_error (GRUB_ERR_IO, "couldn't commit pageflip");
+
+  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;
+    }
+
+  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 grub_video_fb_fini ();
+  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 (int enable)
+{
+  grub_err_t err;
+
+  if (enable)
+    {
+      err = doublebuf_pageflipping_init ();
+      if (!err)
+       {
+         framebuffer.mode_info.mode_type
+           |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+         return GRUB_ERR_NONE;
+       }
+
+      grub_errno = GRUB_ERR_NONE;
+
+      err = grub_video_fb_doublebuf_blit_init (&framebuffer.front_target,
+                                              &framebuffer.back_target,
+                                              &framebuffer.update_screen,
+                                              framebuffer.mode_info,
+                                              framebuffer.ptr);
+
+      if (!err)
+       {
+         framebuffer.mode_info.mode_type
+           |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+         return GRUB_ERR_NONE;
+       }
+
+      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)
 {
@@ -506,12 +672,15 @@ grub_video_vbe_setup (unsigned int width, unsigned int 
height,
 
       framebuffer.mode_info.blit_format = grub_video_get_blit_format 
(&framebuffer.mode_info);
 
-      err = grub_video_fb_create_render_target_from_pointer 
(&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr);
-
+      /* Set up double buffering and targets.  */
+      err = double_buffering_init (!(mode_mask
+                                    & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)
+                                  || (mode_type
+                                      & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED));
       if (err)
        return err;
 
-      err = grub_video_fb_set_active_render_target (framebuffer.render_target);
+      err = grub_video_fb_set_active_render_target (framebuffer.back_target);
 
       if (err)
        return err;
@@ -548,7 +717,15 @@ grub_video_vbe_set_palette (unsigned int start, unsigned 
int count,
 static grub_err_t
 grub_video_vbe_swap_buffers (void)
 {
-  /* TODO: Implement buffer swapping.  */
+  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;
 }
 
@@ -556,27 +733,42 @@ 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.render_target;
+      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;
+  *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;
 }
 
-
 static struct grub_video_adapter grub_video_vbe_adapter =
   {
     .name = "VESA BIOS Extension Video Driver",
@@ -602,7 +794,7 @@ static struct grub_video_adapter grub_video_vbe_adapter =
     .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_fb_get_active_render_target,
+    .get_active_render_target = grub_video_vbe_get_active_render_target,
 
     .next = 0
   };
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to