All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] GSoC #07 VBE double buffering
@ 2008-08-31  6:58 Colin D Bennett
  2008-10-05  4:43 ` [PATCH] GSoC #07 VBE double buffering (vs r1885) Colin D Bennett
  0 siblings, 1 reply; 10+ messages in thread
From: Colin D Bennett @ 2008-08-31  6:58 UTC (permalink / raw)
  To: grub-devel


[-- Attachment #1.1: Type: text/plain, Size: 236 bytes --]

This patch adds double buffering support to the VBE video driver.  It
uses page flipping if possible, and falls back on using an offscreen
buffer which is blitted to video memory if page flipping is not
possible.

Regards,
Colin

[-- Attachment #1.2: 07_ChangeLog.txt --]
[-- Type: text/plain, Size: 1334 bytes --]

2008-08-30  Colin D Bennett <colin@gibibit.com>

	VBE smart double buffering using page flipping or blitting.

	* include/grub/video.h (GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER):
	Removed.
	(GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER): Removed.

	* video/i386/pc/vbe.c (framebuffer): Added is_double_buffered field.
	(doublebuf_state): New struct.
	(double_buffering_init): New function.
	(grub_video_vbe_init): Clear doublebuf_state.
	(grub_video_vbe_fini): Destroy doublebuf_state.
	(grub_video_vbe_setup): Call double_buffering_init to initialize
	double buffering.
	(doublebuf_pageflipping_commit): New function.
	(doublebuf_pageflipping_update_screen): New function.
	(doublebuf_pageflipping_destroy): New function.
	(doublebuf_pageflipping_init): New function.
	(doublebuf_blit_update_screen): New function.
	(doublebuf_blit_destroy): New function.
	(doublebuf_blit_init): New function.
	(doublebuf_null_update_screen): New function.
	(doublebuf_null_destroy): New function.
	(doublebuf_null_init): New function.
	(double_buffering_init): New function.
	(grub_video_vbe_swap_buffers): Implement buffer swapping.
	(grub_video_vbe_set_active_render_target): Handle special target
	GRUB_VIDEO_RENDER_TARGET_DISPLAY and neither
	GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER nor
	GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.3: 07_vbe-doublebuf.patch --]
[-- Type: text/x-patch; name=07_vbe-doublebuf.patch, Size: 9180 bytes --]

=== modified file 'include/grub/video.h'
--- include/grub/video.h	2008-08-31 01:02:11 +0000
+++ include/grub/video.h	2008-08-31 06:19:25 +0000
@@ -47,10 +47,11 @@
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK		0x0000ff00
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS		8
 
-/* Defined predefined render targets.  */
-#define GRUB_VIDEO_RENDER_TARGET_DISPLAY	((struct grub_video_render_target *) 0)
-#define GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER	((struct grub_video_render_target *) 0)
-#define GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER	((struct grub_video_render_target *) 1)
+/* Predefined render target: */
+/* The render target that client code should render to. */
+#define GRUB_VIDEO_RENDER_TARGET_DISPLAY \
+  ((struct grub_video_render_target *) 0)
+
 
 /* Defined blitting formats.  */
 enum grub_video_blit_format
@@ -267,6 +268,9 @@
 
 grub_err_t grub_video_scroll (grub_video_color_t color, int dx, int dy);
 
+/* Swap the pages referred to by the front buffer and back buffer render
+ * targets, and the page previously referred to by the back buffer is made
+ * visible on the display. */
 grub_err_t grub_video_swap_buffers (void);
 
 grub_err_t grub_video_create_render_target (struct grub_video_render_target **result,

=== modified file 'video/i386/pc/vbe.c'
--- video/i386/pc/vbe.c	2008-08-31 01:07:22 +0000
+++ video/i386/pc/vbe.c	2008-08-31 06:19:26 +0000
@@ -63,6 +63,7 @@
 static struct
 {
   struct grub_video_render_target render_target;
+  int is_double_buffered;       /* Is the video mode double buffered? */
 
   unsigned int bytes_per_scan_line;
   unsigned int bytes_per_pixel;
@@ -77,6 +78,24 @@
 static grub_uint32_t mode_in_use = 0x55aa;
 static grub_uint16_t *mode_list;
 
+static struct 
+{
+  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. */
+
+  /* For blit strategy. */
+  grub_uint8_t *offscreen_buffer;
+
+  /* Virtual functions. */
+  int (*update_screen) (void);
+  int (*destroy) (void);
+} doublebuf_state;
+
+static void double_buffering_init (void);
+
 static void *
 real2pm (grub_vbe_farptr_t ptr)
 {
@@ -376,6 +395,7 @@
   /* Reset frame buffer and render target variables.  */
   grub_memset (&framebuffer, 0, sizeof(framebuffer));
   render_target = &framebuffer.render_target;
+  grub_memset (&doublebuf_state, 0, sizeof(doublebuf_state));
 
   return GRUB_ERR_NONE;
 }
@@ -391,6 +411,9 @@
     /* TODO: Decide, is this something we want to do.  */
     return grub_errno;
 
+  if (doublebuf_state.destroy)
+    doublebuf_state.destroy();
+
   /* TODO: Free any resources allocated by driver.  */
   grub_free (mode_list);
   mode_list = 0;
@@ -533,9 +556,13 @@
       render_target->viewport.width = active_mode_info.x_resolution;
       render_target->viewport.height = active_mode_info.y_resolution;
 
-      /* Set framebuffer pointer and mark it as non allocated.  */
+      /* Mark framebuffer memory as non allocated.  */
       render_target->is_allocated = 0;
-      render_target->data = framebuffer.ptr;
+
+      /* Set up double buffering information. */
+      framebuffer.is_double_buffered =
+        ((mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) != 0);
+      double_buffering_init ();
 
       /* Copy default palette to initialize emulated palette.  */
       for (i = 0; 
@@ -556,6 +583,166 @@
   return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found.");
 }
 
+/* 
+   Set framebuffer render target page and display the proper page, based on
+   `doublebuf_state.render_page' and `doublebuf_state.displayed_page',
+   respectively. 
+
+   Returns 0 upon success, nonzero upon failure.
+ */
+static int
+doublebuf_pageflipping_commit (void)
+{
+  /* Set the render target's data pointer to the start of the render_page. */
+  framebuffer.render_target.data =
+    ((char *) framebuffer.ptr) +
+    doublebuf_state.page_size * doublebuf_state.render_page;
+
+  /* Tell the video adapter to display the new front page. */
+  int display_start_line =
+    framebuffer.render_target.mode_info.height 
+    * doublebuf_state.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 1;
+
+  return 0;
+}
+
+static int
+doublebuf_pageflipping_update_screen (void)
+{
+  /* Swap the page numbers in the framebuffer struct. */
+  int new_displayed_page = doublebuf_state.render_page;
+  doublebuf_state.render_page = doublebuf_state.displayed_page;
+  doublebuf_state.displayed_page = new_displayed_page;
+
+  return doublebuf_pageflipping_commit ();
+}
+
+static int
+doublebuf_pageflipping_destroy (void)
+{
+  doublebuf_state.update_screen = 0;
+  doublebuf_state.destroy = 0;
+  return 0;
+}
+
+static int
+doublebuf_pageflipping_init (void)
+{
+  doublebuf_state.page_size =
+    framebuffer.bytes_per_scan_line * render_target->mode_info.height;
+  
+  /* Get video RAM size in bytes.  */
+  grub_size_t vram_size = controller_info.total_memory << 16;
+
+  if (2 * doublebuf_state.page_size > vram_size)
+    return 1;   /* Not enough video memory for 2 pages. */
+
+  doublebuf_state.displayed_page = 0;
+  doublebuf_state.render_page = 1;
+
+  doublebuf_state.update_screen = doublebuf_pageflipping_update_screen;
+  doublebuf_state.destroy = doublebuf_pageflipping_destroy;
+
+  /* Set the framebuffer memory data pointer and display the right page. */
+  if (doublebuf_pageflipping_commit () != GRUB_ERR_NONE)
+    return 1;   /* Unable to set the display start.  */
+
+  framebuffer.render_target.mode_info.mode_type
+    |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+  return 0;
+}
+
+static int
+doublebuf_blit_update_screen (void)
+{
+  grub_memcpy (framebuffer.ptr,
+               doublebuf_state.offscreen_buffer, 
+               doublebuf_state.page_size);
+  return 0;
+}
+
+static int
+doublebuf_blit_destroy (void)
+{
+  grub_free (doublebuf_state.offscreen_buffer);
+  doublebuf_state.offscreen_buffer = 0;
+
+  doublebuf_state.update_screen = 0;
+  doublebuf_state.destroy = 0;
+  return 0;
+}
+
+static int
+doublebuf_blit_init (void)
+{
+  doublebuf_state.page_size =
+    framebuffer.bytes_per_scan_line * render_target->mode_info.height;
+
+  doublebuf_state.offscreen_buffer = (grub_uint8_t *)
+    grub_malloc (doublebuf_state.page_size);
+  if (doublebuf_state.offscreen_buffer == 0)
+    return 1;   /* Error.  */
+
+  framebuffer.render_target.data = doublebuf_state.offscreen_buffer;
+  doublebuf_state.update_screen = doublebuf_blit_update_screen;
+  doublebuf_state.destroy = doublebuf_blit_destroy;
+
+  framebuffer.render_target.mode_info.mode_type
+    |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+  return 0;
+}
+
+static int
+doublebuf_null_update_screen (void)
+{
+  return 0;
+}
+
+static int
+doublebuf_null_destroy (void)
+{
+  doublebuf_state.update_screen = 0;
+  doublebuf_state.destroy = 0;
+  return 0;
+}
+
+static int
+doublebuf_null_init (void)
+{
+  framebuffer.render_target.data = framebuffer.ptr;
+  doublebuf_state.update_screen = doublebuf_null_update_screen;
+  doublebuf_state.destroy = doublebuf_null_destroy;
+
+  framebuffer.render_target.mode_info.mode_type
+    &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+  return 0;
+}
+
+/* Select the best double buffering mode available.  */
+static void
+double_buffering_init (void)
+{
+  if (doublebuf_state.destroy)
+    doublebuf_state.destroy();
+
+  if (framebuffer.is_double_buffered)
+    {
+      if (doublebuf_pageflipping_init () == 0)
+        return;
+
+      if (doublebuf_blit_init () == 0)
+        return;
+    }
+
+  /* Fall back to no double buffering. */
+  doublebuf_null_init ();
+}
+
 static grub_err_t
 grub_video_vbe_get_info (struct grub_video_mode_info *mode_info)
 {
@@ -1485,10 +1672,13 @@
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t 
+static grub_err_t
 grub_video_vbe_swap_buffers (void)
 {
-  /* TODO: Implement buffer swapping.  */
+  if (doublebuf_state.update_screen () != 0)
+    return grub_error (GRUB_ERR_INVALID_COMMAND,
+                       "Double buffer update failed");
+
   return GRUB_ERR_NONE;
 }
 
@@ -1587,17 +1777,13 @@
 static grub_err_t
 grub_video_vbe_set_active_render_target (struct grub_video_render_target *target)
 {
-  if (target == GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER)
+  if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
     {
       render_target = &framebuffer.render_target;
-
+      
       return GRUB_ERR_NONE;
     }
 
-  if (target == GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER)
-    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, 
-                       "double buffering not implemented yet.");
-
   if (! target->data)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, 
                        "invalid render target given.");


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-08-31  6:58 [PATCH] GSoC #07 VBE double buffering Colin D Bennett
@ 2008-10-05  4:43 ` Colin D Bennett
  2008-10-05  8:52   ` Vesa Jääskeläinen
  0 siblings, 1 reply; 10+ messages in thread
From: Colin D Bennett @ 2008-10-05  4:43 UTC (permalink / raw)
  To: grub-devel


[-- Attachment #1.1: Type: text/plain, Size: 65 bytes --]

Clean patch against trunk SVN revision 1885.

Regards,
Colin

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: 07_vbe-doublebuf_r1885.patch --]
[-- Type: text/x-patch; name=07_vbe-doublebuf_r1885.patch, Size: 8733 bytes --]

=== modified file 'include/grub/video.h'
--- include/grub/video.h	2008-09-07 14:55:58 +0000
+++ include/grub/video.h	2008-10-05 04:29:17 +0000
@@ -47,10 +47,10 @@
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK		0x0000ff00
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS		8
 
-/* Defined predefined render targets.  */
-#define GRUB_VIDEO_RENDER_TARGET_DISPLAY	((struct grub_video_render_target *) 0)
-#define GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER	((struct grub_video_render_target *) 0)
-#define GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER	((struct grub_video_render_target *) 1)
+/* 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)
 
 /* Defined blitting formats.  */
 enum grub_video_blit_format

=== modified file 'video/i386/pc/vbe.c'
--- video/i386/pc/vbe.c	2008-09-07 14:55:58 +0000
+++ video/i386/pc/vbe.c	2008-10-05 04:29:17 +0000
@@ -63,6 +63,7 @@
 static struct
 {
   struct grub_video_render_target render_target;
+  int is_double_buffered;       /* Is the video mode double buffered? */
 
   unsigned int bytes_per_scan_line;
   unsigned int bytes_per_pixel;
@@ -77,6 +78,24 @@
 static grub_uint32_t mode_in_use = 0x55aa;
 static grub_uint16_t *mode_list;
 
+static struct 
+{
+  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. */
+
+  /* For blit strategy. */
+  grub_uint8_t *offscreen_buffer;
+
+  /* Virtual functions. */
+  int (*update_screen) (void);
+  int (*destroy) (void);
+} doublebuf_state;
+
+static void double_buffering_init (void);
+
 static void *
 real2pm (grub_vbe_farptr_t ptr)
 {
@@ -376,6 +395,7 @@
   /* Reset frame buffer and render target variables.  */
   grub_memset (&framebuffer, 0, sizeof(framebuffer));
   render_target = &framebuffer.render_target;
+  grub_memset (&doublebuf_state, 0, sizeof(doublebuf_state));
 
   return GRUB_ERR_NONE;
 }
@@ -391,6 +411,9 @@
     /* TODO: Decide, is this something we want to do.  */
     return grub_errno;
 
+  if (doublebuf_state.destroy)
+    doublebuf_state.destroy();
+
   /* TODO: Free any resources allocated by driver.  */
   grub_free (mode_list);
   mode_list = 0;
@@ -533,9 +556,12 @@
       render_target->viewport.width = active_mode_info.x_resolution;
       render_target->viewport.height = active_mode_info.y_resolution;
 
-      /* Set framebuffer pointer and mark it as non allocated.  */
+      /* Mark framebuffer memory as non allocated.  */
       render_target->is_allocated = 0;
-      render_target->data = framebuffer.ptr;
+      /* Set up double buffering information. */
+      framebuffer.is_double_buffered =
+        ((mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) != 0);
+      double_buffering_init ();
 
       /* Copy default palette to initialize emulated palette.  */
       for (i = 0;
@@ -556,6 +582,166 @@
   return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found.");
 }
 
+/* 
+   Set framebuffer render target page and display the proper page, based on
+   `doublebuf_state.render_page' and `doublebuf_state.displayed_page',
+   respectively. 
+
+   Returns 0 upon success, nonzero upon failure.
+ */
+static int
+doublebuf_pageflipping_commit (void)
+{
+  /* Set the render target's data pointer to the start of the render_page. */
+  framebuffer.render_target.data =
+    ((char *) framebuffer.ptr) +
+    doublebuf_state.page_size * doublebuf_state.render_page;
+
+  /* Tell the video adapter to display the new front page. */
+  int display_start_line =
+    framebuffer.render_target.mode_info.height 
+    * doublebuf_state.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 1;
+
+  return 0;
+}
+
+static int
+doublebuf_pageflipping_update_screen (void)
+{
+  /* Swap the page numbers in the framebuffer struct. */
+  int new_displayed_page = doublebuf_state.render_page;
+  doublebuf_state.render_page = doublebuf_state.displayed_page;
+  doublebuf_state.displayed_page = new_displayed_page;
+
+  return doublebuf_pageflipping_commit ();
+}
+
+static int
+doublebuf_pageflipping_destroy (void)
+{
+  doublebuf_state.update_screen = 0;
+  doublebuf_state.destroy = 0;
+  return 0;
+}
+
+static int
+doublebuf_pageflipping_init (void)
+{
+  doublebuf_state.page_size =
+    framebuffer.bytes_per_scan_line * render_target->mode_info.height;
+  
+  /* Get video RAM size in bytes.  */
+  grub_size_t vram_size = controller_info.total_memory << 16;
+
+  if (2 * doublebuf_state.page_size > vram_size)
+    return 1;   /* Not enough video memory for 2 pages. */
+
+  doublebuf_state.displayed_page = 0;
+  doublebuf_state.render_page = 1;
+
+  doublebuf_state.update_screen = doublebuf_pageflipping_update_screen;
+  doublebuf_state.destroy = doublebuf_pageflipping_destroy;
+
+  /* Set the framebuffer memory data pointer and display the right page. */
+  if (doublebuf_pageflipping_commit () != GRUB_ERR_NONE)
+    return 1;   /* Unable to set the display start.  */
+
+  framebuffer.render_target.mode_info.mode_type
+    |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+  return 0;
+}
+
+static int
+doublebuf_blit_update_screen (void)
+{
+  grub_memcpy (framebuffer.ptr,
+               doublebuf_state.offscreen_buffer, 
+               doublebuf_state.page_size);
+  return 0;
+}
+
+static int
+doublebuf_blit_destroy (void)
+{
+  grub_free (doublebuf_state.offscreen_buffer);
+  doublebuf_state.offscreen_buffer = 0;
+
+  doublebuf_state.update_screen = 0;
+  doublebuf_state.destroy = 0;
+  return 0;
+}
+
+static int
+doublebuf_blit_init (void)
+{
+  doublebuf_state.page_size =
+    framebuffer.bytes_per_scan_line * render_target->mode_info.height;
+
+  doublebuf_state.offscreen_buffer = (grub_uint8_t *)
+    grub_malloc (doublebuf_state.page_size);
+  if (doublebuf_state.offscreen_buffer == 0)
+    return 1;   /* Error.  */
+
+  framebuffer.render_target.data = doublebuf_state.offscreen_buffer;
+  doublebuf_state.update_screen = doublebuf_blit_update_screen;
+  doublebuf_state.destroy = doublebuf_blit_destroy;
+
+  framebuffer.render_target.mode_info.mode_type
+    |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+  return 0;
+}
+
+static int
+doublebuf_null_update_screen (void)
+{
+  return 0;
+}
+
+static int
+doublebuf_null_destroy (void)
+{
+  doublebuf_state.update_screen = 0;
+  doublebuf_state.destroy = 0;
+  return 0;
+}
+
+static int
+doublebuf_null_init (void)
+{
+  framebuffer.render_target.data = framebuffer.ptr;
+  doublebuf_state.update_screen = doublebuf_null_update_screen;
+  doublebuf_state.destroy = doublebuf_null_destroy;
+
+  framebuffer.render_target.mode_info.mode_type
+    &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+  return 0;
+}
+
+/* Select the best double buffering mode available.  */
+static void
+double_buffering_init (void)
+{
+  if (doublebuf_state.destroy)
+    doublebuf_state.destroy();
+
+  if (framebuffer.is_double_buffered)
+    {
+      if (doublebuf_pageflipping_init () == 0)
+        return;
+
+      if (doublebuf_blit_init () == 0)
+        return;
+    }
+
+  /* Fall back to no double buffering. */
+  doublebuf_null_init ();
+}
+
 static grub_err_t
 grub_video_vbe_get_info (struct grub_video_mode_info *mode_info)
 {
@@ -1475,7 +1661,10 @@
 static grub_err_t
 grub_video_vbe_swap_buffers (void)
 {
-  /* TODO: Implement buffer swapping.  */
+  if (doublebuf_state.update_screen () != 0)
+    return grub_error (GRUB_ERR_INVALID_COMMAND,
+                       "Double buffer update failed");
+
   return GRUB_ERR_NONE;
 }
 
@@ -1574,17 +1763,13 @@
 static grub_err_t
 grub_video_vbe_set_active_render_target (struct grub_video_render_target *target)
 {
-  if (target == GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER)
+  if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
     {
       render_target = &framebuffer.render_target;
 
       return GRUB_ERR_NONE;
     }
 
-  if (target == GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER)
-    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                       "double buffering not implemented yet.");
-
   if (! target->data)
     return grub_error (GRUB_ERR_BAD_ARGUMENT,
                        "invalid render target given.");


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-05  4:43 ` [PATCH] GSoC #07 VBE double buffering (vs r1885) Colin D Bennett
@ 2008-10-05  8:52   ` Vesa Jääskeläinen
  2008-10-05 16:16     ` Colin D Bennett
  0 siblings, 1 reply; 10+ messages in thread
From: Vesa Jääskeläinen @ 2008-10-05  8:52 UTC (permalink / raw)
  To: The development of GRUB 2

Colin D Bennett wrote:
> Clean patch against trunk SVN revision 1885.
> 
> Regards,
> Colin

Thanks for the re-base.

In your opinion how should rendering of double buffered screen be
different from single buffered?

Eg. who is responsible to handle those differences?

Thanks,
Vesa Jääskeläinen



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-05  8:52   ` Vesa Jääskeläinen
@ 2008-10-05 16:16     ` Colin D Bennett
  0 siblings, 0 replies; 10+ messages in thread
From: Colin D Bennett @ 2008-10-05 16:16 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 3158 bytes --]

On Sun, 05 Oct 2008 11:52:08 +0300
Vesa J____skel__inen <chaac@nic.fi> wrote:

> Colin D Bennett wrote:
> > Clean patch against trunk SVN revision 1885.
> > 
> > Regards,
> > Colin
> 
> Thanks for the re-base.
> 
> In your opinion how should rendering of double buffered screen be
> different from single buffered?
> 
> Eg. who is responsible to handle those differences?
> 
> Thanks,
> Vesa J__skel_inen

Generally, the client code should (IMO) do the following, which will
work for both a double-buffered and a non-double-buffered video mode:
(by 'client', I mean the code that is using the video API)

  // Render the graphical elements to the back buffer.
  grub_video_fill_rect (...);
  grub_video_draw_string (...);
  grub_video_blit_bitmap (...);
  // Make the back buffer contents visible.
  grub_video_swap_buffers ();    // NOP in single buffered mode

For double buffered mode, this properly renders the graphics using
double buffering.  For non double buffered mode, the "back buffer" and
the "front buffer" are one and the same, and the 'swap_buffers'
operation does nothing.

I think the main difference between rendering to a single buffered
versus a double buffered screen is the fact that when rendering using
single buffering, the client code can update only the changed parts of
the screen, but for double buffering it must update the whole screen.

In single buffered mode, you can incrementally modify the video memory
contents based on the last rendered frame.  However, in double buffered
mode the last rendered frame might not be in the back buffer after
calling swap_buffers, depending on the double buffering strategy
selected at runtime:

1. when page flipping is in use, the back buffer will contain the
   contents of the screen before the *prior* call to swap_buffers.

2. when the back buffer is in main memory (the blit strategy), the back
   buffer will actually retain its contents after swap_buffers, since
   the buffers cannot actually be swapped (thus perhaps the
   'swap_buffers' name is not the most accurate description of the
   essence of this function, but it gets the general message across).

In the gfxmenu code, I decided that, at least to start with, to always
render complete frames on the screen.  In this way, the code functions
properly whether or not double buffering is in use, and it is kept
simple.  This worked well for the menu screen, and has good performance
in my tests even on my 1 GHz VIA C3 machine.  I figure that it's more
important to get it working properly, and then optimize the
performance later where it might be needed.

The main current performance problem lies in the way that the gfxterm
works when embedded in the double buffered gfxmenu screen. It is causing
a full screen redraw every time a character or a string is changed on
the terminal. I've thought about this a lot in the past and I don't
have a simple answer, though I have a lot of ideas (switch to single
buffering temporarily (--), pre-render a bitmap of the rest of
the screen (-), coalesce updates to the gfxterm (++), ...).

Regards,
Colin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
       [not found] <1745824252.39921223234235570.JavaMail.root@aczmb1>
@ 2008-10-05 19:17 ` Andy Goth
  2008-10-05 19:47   ` Colin D Bennett
  0 siblings, 1 reply; 10+ messages in thread
From: Andy Goth @ 2008-10-05 19:17 UTC (permalink / raw)
  To: The development of GRUB 2

"Colin D Bennett" <colin@gibibit.com> wrote:
> However, in double buffered mode the last rendered frame might 
> not be in the back buffer after calling swap_buffers, depending 
> on the double buffering strategy selected at runtime:
> 
> 1. page flipping is in use
> 2. back buffer is in main memory

With a little bookkeeping you can avoid having to redraw the whole screen.  Here are some strategies:

(double buffering with page flipping and drawing to main memory)

Draw to a buffer in main memory.  Maintain a list of changed regions.  This is your list of dirty rectangles.  After completing a frame, blit the dirty rectangles to the page that's about to get flipped into view.  Age all dirty rectangles by one frame, and remove all dirty rectangles that were previously aged.  If the dirty rectangle list gets too long, just blit the whole screen.  Clever algorithms can combine dirty rectangles to save memory.

(double buffering with page flipping without drawing to main memory)

Draw to the back buffer, and maintain a dirty rectangle list.  Immediately after vertical retrace start, flip pages, and blit the dirty rectangles to the new back buffer from the (now being displayed) old back buffer.  Empty the dirty rectangle list.

(double buffering without page flipping)

Draw to a buffer in main memory, and maintain a dirty rectangle list.  During vertical retrace, blit the dirty rectangles to VRAM.  Empty the dirty rectangle list.

(single buffering with minimal shearing)

Draw to VRAM.  Wait for the start of vertical retrace before drawing.  Hopefully all drawing can complete before the monitor gets repainted.

(single buffering)

Draw to VRAM.

Disclaimer: I don't actually know what you're trying to do (plain text, drop shadows, animation, 3D graphics... no clue), so the stuff I suggest might be overkill.  Nevertheless, I hope it is helpful.

-- 
Andy Goth | http://andy.junkdrome.org/
unununium@{aircanopy.net,openverse.com}



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-05 19:17 ` Andy Goth
@ 2008-10-05 19:47   ` Colin D Bennett
  2008-10-05 19:57     ` Andy Goth
  0 siblings, 1 reply; 10+ messages in thread
From: Colin D Bennett @ 2008-10-05 19:47 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 2555 bytes --]

On Sun, 5 Oct 2008 14:17:42 -0500 (CDT)
Andy Goth <unununium@aircanopy.net> wrote:

> "Colin D Bennett" <colin@gibibit.com> wrote:
> > However, in double buffered mode the last rendered frame might 
> > not be in the back buffer after calling swap_buffers, depending 
> > on the double buffering strategy selected at runtime:
> > 
> > 1. page flipping is in use
> > 2. back buffer is in main memory
> 
> With a little bookkeeping you can avoid having to redraw the whole
> screen.  Here are some strategies:
> 
> (double buffering with page flipping and drawing to main memory)
> 
> Draw to a buffer in main memory.  Maintain a list of changed
> regions.  This is your list of dirty rectangles.  After completing a
> frame, blit the dirty rectangles to the page that's about to get
> flipped into view.  Age all dirty rectangles by one frame, and remove
> all dirty rectangles that were previously aged.  If the dirty
> rectangle list gets too long, just blit the whole screen.  Clever
> algorithms can combine dirty rectangles to save memory.
> 
> (double buffering with page flipping without drawing to main memory)
> 
> Draw to the back buffer, and maintain a dirty rectangle list.
> Immediately after vertical retrace start, flip pages, and blit the
> dirty rectangles to the new back buffer from the (now being
> displayed) old back buffer.  Empty the dirty rectangle list.
> 
> (double buffering without page flipping)
> 
> Draw to a buffer in main memory, and maintain a dirty rectangle
> list.  During vertical retrace, blit the dirty rectangles to VRAM.
> Empty the dirty rectangle list.
> 
> (single buffering with minimal shearing)
> 
> Draw to VRAM.  Wait for the start of vertical retrace before
> drawing.  Hopefully all drawing can complete before the monitor gets
> repainted.
> 
> (single buffering)
> 
> Draw to VRAM.
> 
> Disclaimer: I don't actually know what you're trying to do (plain
> text, drop shadows, animation, 3D graphics... no clue), so the stuff
> I suggest might be overkill.  Nevertheless, I hope it is helpful.

Thanks for the ideas.  Also, I know it is possible to write code that
is optimal for each case, my plan is to avoid complicated dirty-region
repaint strategies at first.  ("First make it work, then make it
rock.")  After everything is working, then we can profile and do
performance optimization.  After everything is functioning and
performance bottlenecks are identified, then we can perhaps use some
techniques like you suggest.

Thanks,
Colin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-05 19:47   ` Colin D Bennett
@ 2008-10-05 19:57     ` Andy Goth
  2008-10-06 19:02       ` Vesa Jääskeläinen
  0 siblings, 1 reply; 10+ messages in thread
From: Andy Goth @ 2008-10-05 19:57 UTC (permalink / raw)
  To: The development of GRUB 2

"Colin D Bennett" <colin@gibibit.com> wrote:
> First make it work, then make it rock.

I certainly do appreciate that, but...

> my plan is to avoid complicated dirty-region repaint strategies 
> at first.

Requiring full-screen repaints is an architectural design that might need rework to undo later.  Or might not, depending on how you implement it.  The interim approach you choose now should be informed by foresight.

Full-screen repaint approach that will require rework later:

- One function draws everything in sequence.  It gets called every frame.

Full-screen repaint approach that will be easy to integrate into a partial update scheme:

- A separate function exists for drawing each logical unit of the screen.
- Every frame, one function calls all the separate functions in sequence.

Or something like that.

-- 
Andy Goth | http://andy.junkdrome.org/
unununium@{aircanopy.net,openverse.com}



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-05 19:57     ` Andy Goth
@ 2008-10-06 19:02       ` Vesa Jääskeläinen
  2008-10-13 16:48         ` Colin D Bennett
  0 siblings, 1 reply; 10+ messages in thread
From: Vesa Jääskeläinen @ 2008-10-06 19:02 UTC (permalink / raw)
  To: The development of GRUB 2

Andy Goth wrote:
> "Colin D Bennett" <colin@gibibit.com> wrote:
>> First make it work, then make it rock.
> 
> I certainly do appreciate that, but...
> 
>> my plan is to avoid complicated dirty-region repaint strategies 
>> at first.
> 
> Requiring full-screen repaints is an architectural design that might need rework to undo later.  Or might not, depending on how you implement it.  The interim approach you choose now should be informed by foresight.
> 
> Full-screen repaint approach that will require rework later:
> 
> - One function draws everything in sequence.  It gets called every frame.
> 
> Full-screen repaint approach that will be easy to integrate into a partial update scheme:
> 
> - A separate function exists for drawing each logical unit of the screen.
> - Every frame, one function calls all the separate functions in sequence.
> 
> Or something like that.

Hi,

I would like to thank Andy for his comments on the topic. I do share the
same concern about designing double buffering to work properly without
making hasty implementation first. We can use non-buffered solution as a
first stage "hasty implementation" and design good enough solution to
handle double buffering well.

Dirty rectangles for every buffer (two in double buffer case) might be
the best approach here. I think there is some kind of dirty rectangle
implementation on gfxterm so that could be used as a base for this work.
After that it could be improved to increase performance but the main
point being that there has to be proper framework to make it work properly.

Usually the slowest step is to transfer image data from main memory to
video memory (and usually the slowest is to read from video memory to
main memory and then save it back to video memory). If we can optimize
memory copies between video and main memories we are on good track to
solve speed problem.

Thanks,
Vesa Jääskeläinen



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-06 19:02       ` Vesa Jääskeläinen
@ 2008-10-13 16:48         ` Colin D Bennett
  2008-10-13 17:12           ` Colin D Bennett
  0 siblings, 1 reply; 10+ messages in thread
From: Colin D Bennett @ 2008-10-13 16:48 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 2175 bytes --]

On Mon, 06 Oct 2008 22:02:46 +0300
Vesa Jääskeläinen <chaac@nic.fi> wrote:

> Andy Goth wrote:
> > "Colin D Bennett" <colin@gibibit.com> wrote:
> > Requiring full-screen repaints is an architectural design that
> > might need rework to undo later.  Or might not, depending on how
> > you implement it.  The interim approach you choose now should be
> > informed by foresight.
> [...]
> Hi,
> 
> I would like to thank Andy for his comments on the topic. I do share
> the same concern about designing double buffering to work properly
> without making hasty implementation first. We can use non-buffered
> solution as a first stage "hasty implementation" and design good
> enough solution to handle double buffering well.
> 
> Dirty rectangles for every buffer (two in double buffer case) might be
> the best approach here. I think there is some kind of dirty rectangle
> implementation on gfxterm so that could be used as a base for this
> work. After that it could be improved to increase performance but the
> main point being that there has to be proper framework to make it
> work properly.
> 
> Usually the slowest step is to transfer image data from main memory to
> video memory (and usually the slowest is to read from video memory to
> main memory and then save it back to video memory). If we can optimize
> memory copies between video and main memories we are on good track to
> solve speed problem.

I agree that a dirty region repaint management system will provide
better performance, but I certainly do not have time to implement it
now.  IMO that would take a lot of work.

I *am* interested in getting high performance, but
(1) I simply don't have time to do it myself right now, and 
(2) gfxmenu seems plenty fast to me--I have run Lua-driven full screen
animations as a desktop background for gfxmenu, and it worked
fantastically. The menu system performs decently even on my
ultra-low-power-but-low-performance-too Via C3 system.

Granted, the gfxterm terminal is nearly unusable right now, but I think
its performance can be improved significantly through a few relatively
minor changes.

Regards,
Colin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] GSoC #07 VBE double buffering (vs r1885)
  2008-10-13 16:48         ` Colin D Bennett
@ 2008-10-13 17:12           ` Colin D Bennett
  0 siblings, 0 replies; 10+ messages in thread
From: Colin D Bennett @ 2008-10-13 17:12 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 1183 bytes --]

On Mon, 13 Oct 2008 09:48:15 -0700
Colin D Bennett <colin@gibibit.com> wrote:

> I *am* interested in getting high performance, but
> (1) I simply don't have time to do it myself right now, and 
> (2) gfxmenu seems plenty fast to me--I have run Lua-driven full screen
> animations as a desktop background for gfxmenu, and it worked
> fantastically. The menu system performs decently even on my
> ultra-low-power-but-low-performance-too Via C3 system.

Another reason I did not implement dirty region repaint optimization
that I forgot to mention:

(3) Dirty region repaint handling does not help at all when most of the
screen is being updated on each frame.  Since I expect the "fancy menu"
to become more and more full of eye-candy (that is the purpose, after
all!), the dirty region handling would only help for the most vanilla
of themes.  I have said I will add some animation and special
effects to GRUB.  Things that I think would be cool (and cool => more
people drawn to use GRUB) include a fade-in/fade-out when GRUB
starts/executes an entry, a fully animated background of some sort (of
the type that many xscreensavers use), etc.

Regards,
Colin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2008-10-13 17:13 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-31  6:58 [PATCH] GSoC #07 VBE double buffering Colin D Bennett
2008-10-05  4:43 ` [PATCH] GSoC #07 VBE double buffering (vs r1885) Colin D Bennett
2008-10-05  8:52   ` Vesa Jääskeläinen
2008-10-05 16:16     ` Colin D Bennett
     [not found] <1745824252.39921223234235570.JavaMail.root@aczmb1>
2008-10-05 19:17 ` Andy Goth
2008-10-05 19:47   ` Colin D Bennett
2008-10-05 19:57     ` Andy Goth
2008-10-06 19:02       ` Vesa Jääskeläinen
2008-10-13 16:48         ` Colin D Bennett
2008-10-13 17:12           ` Colin D Bennett

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.