From: Colin D Bennett <colin@gibibit.com>
To: grub-devel@gnu.org
Subject: [PATCH] GSoC #07 VBE double buffering
Date: Sat, 30 Aug 2008 23:58:33 -0700 [thread overview]
Message-ID: <20080830235833.3b29b3c2@gamma.lan> (raw)
[-- 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 --]
next reply other threads:[~2008-08-31 8:45 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-08-31 6:58 Colin D Bennett [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080830235833.3b29b3c2@gamma.lan \
--to=colin@gibibit.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.