2008-08-30 Colin D Bennett
Enhance the 'videotest' command with a number of different tests.
* commands/videotest.c (grub_cmd_videotest): Support running different
tests and handling command line arguments for several flags.
(ARGINDEX_TEST_TIME): Added.
(ARGINDEX_DOUBLE_BUF): Likewise.
(arg_options): Likewise.
(DEFAULT_TEST_TIME): Likewise.
(videotest_options): Likewise.
(basic_video_test): Likewise.
(bitmap_demo): Likewise.
(benchmark_config): Likewise.
(MODE_TYPE_RGB): Likewise.
(benchmark_configs): Likewise.
(NUM_BENCHMARK_CONFIGS): Likewise.
(benchmark_result): Likewise.
(BENCHMARK_RESULT_FPS_SCALE): Likewise.
(move_rectangle_one_step): Likewise.
(do_benchmark): Likewise.
(benchmark_test): Likewise.
(clock_test): Likewise.
(doublebuf_test): Likewise.
(text_test): Likewise.
(scale_test): Likewise.
(list_tests): Likewise.
* include/grub/video.h (grub_video_rect_t: Added.
=== modified file 'commands/videotest.c'
--- commands/videotest.c 2008-08-31 03:50:37 +0000
+++ commands/videotest.c 2008-09-01 17:33:56 +0000
@@ -17,6 +17,7 @@
*/
#include
+#include
#include
#include
#include
@@ -26,15 +27,39 @@
#include
#include
#include
-
-static grub_err_t
-grub_cmd_videotest (struct grub_arg_list *state __attribute__ ((unused)),
- int argc __attribute__ ((unused)),
- char **args __attribute__ ((unused)))
-{
+#include
+#include
+#include /* to test grub_vbe_bios_set_display_start */
+
+/* Option array indices. */
+#define ARGINDEX_TEST_TIME 0
+#define ARGINDEX_DOUBLE_BUF 1
+
+static const struct grub_arg_option arg_options[] = {
+ {"time", 't', 0, "Time to run each test, in seconds.", 0, ARG_TYPE_INT},
+ {"dbuf", 'd', 0, "Use double buffered graphics.", 0, ARG_TYPE_NONE},
+ {0, 0, 0, 0, 0, 0}
+};
+
+#define DEFAULT_TEST_TIME 5
+
+/* Command options -- populated base on command line arguments. */
+struct videotest_options
+{
+ int test_time;
+ int double_buffering;
+};
+
+
+static void
+basic_video_test (struct videotest_options *vt_opts)
+{
+ int mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
if (grub_video_setup (1024, 768,
GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != GRUB_ERR_NONE)
- return grub_errno;
+ return;
grub_getkey ();
@@ -68,9 +93,12 @@
font = grub_font_get ("Helvetica Bold 14");
if (! font)
- return grub_error (GRUB_ERR_BAD_FONT, "No font loaded.");
+ {
+ grub_error (GRUB_ERR_BAD_FONT, "No font loaded.");
+ return;
+ }
- glyph = grub_font_get_glyph (font, '*');
+ glyph = grub_font_get_glyph (font, '*');
grub_video_blit_glyph (glyph, color, 200 ,0);
grub_video_set_viewport (x + 150, y + 150,
@@ -106,30 +134,1075 @@
0, 0, width, height);
}
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ /* Test VBE set display start address. */
+ /* This should scroll the screen, first vertically,
+ * then horizontally. The horizontal scrolling seems to
+ * only have a resolution of about 16 pixels on my VIA Mini-ITX. */
+ int vbestatus;
+ int vbeok = 0;
+ int vbeerr = 0;
+ for (i = 0; i < 50; i++)
+ {
+ vbestatus = grub_vbe_bios_set_display_start (0, i);
+ if (vbestatus == GRUB_VBE_STATUS_OK)
+ vbeok++;
+ else
+ vbeerr++;
+ }
+
+ grub_getkey ();
+
+ for (i = 0; i < 50; i++)
+ {
+ vbestatus = grub_vbe_bios_set_display_start (i, 50);
+ if (vbestatus == GRUB_VBE_STATUS_OK)
+ vbeok++;
+ else
+ vbeerr++;
+ }
+
grub_getkey ();
grub_video_delete_render_target (text_layer);
-
- grub_video_restore ();
-
- for (i = 0; i < 16; i++)
- grub_printf("color %d: %08x\n", i, palette[i]);
-
- grub_errno = GRUB_ERR_NONE;
+ grub_video_restore ();
+
+ grub_printf ("VBE set_display_start status: %d\n", vbestatus);
+ grub_printf ("ok: %d\n", vbeok);
+ grub_printf ("errors: %d\n", vbeerr);
+
+ grub_errno = GRUB_ERR_NONE;
+}
+
+
+
+/**
+ * Simple opaque image blit test.
+ * Returns the error status in grub_errno.
+ */
+static void
+bitmap_demo (struct videotest_options *vt_opts)
+{
+ int mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+ if (grub_video_setup (1024, 768, mode_type) != GRUB_ERR_NONE)
+ return;
+
+ grub_video_rect_t view;
+ grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y,
+ (unsigned *) &view.width,
+ (unsigned *) &view.height);
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+ const int bm1width = 500, bm1height = 400;
+ struct grub_video_bitmap *bitmap1;
+
+ if (grub_video_bitmap_create (&bitmap1, bm1width, bm1height,
+ GRUB_VIDEO_BLIT_FORMAT_RGB_888)
+ != GRUB_ERR_NONE)
+ return;
+
+ int offset = 0;
+ int x;
+ int y;
+ grub_uint8_t *data = grub_video_bitmap_get_data (bitmap1);
+ for (y = 0; y < bm1height; y++)
+ {
+ for (x = 0; x < bm1width; x++)
+ {
+ data[offset++] = x ^ y; /* red */
+ data[offset++] = (x * 3) ^ (y * 3); /* green */
+ data[offset++] = (x * 2) ^ (y * 2); /* blue */
+ }
+ }
+
+ /* Blit the entire bitmap in the center of the screen. */
+ grub_video_blit_bitmap (bitmap1,
+ GRUB_VIDEO_BLIT_REPLACE,
+ view.x + (view.width - bm1width) / 2,
+ view.y + (view.height - bm1height) / 2,
+ 0, 0, bm1width, bm1height);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ /* Blit more copies of the bitmap. */
+ /* Upper left. */
+ grub_video_blit_bitmap (bitmap1, GRUB_VIDEO_BLIT_REPLACE,
+ view.x, view.y, 0, 0, bm1width, bm1height);
+ /* Upper right. */
+ grub_video_blit_bitmap (bitmap1,
+ GRUB_VIDEO_BLIT_REPLACE,
+ view.x + view.width - bm1width,
+ view.y, 0, 0, bm1width, bm1height);
+ /* Lower left. */
+ grub_video_blit_bitmap (bitmap1,
+ GRUB_VIDEO_BLIT_REPLACE,
+ view.x,
+ view.y + view.height - bm1height,
+ 0, 0, bm1width, bm1height);
+ /* Lower right. */
+ grub_video_blit_bitmap (bitmap1,
+ GRUB_VIDEO_BLIT_REPLACE,
+ view.x + view.width - bm1width,
+ view.y + view.height - bm1height,
+ 0, 0, bm1width, bm1height);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ int clearbg = 0; /* Boolean flag: whether to fill background. */
+ grub_video_color_t bgcolor = grub_video_map_rgb (16, 16, 96);
+
+ /* Animate the image sliding in. End when a key is pressed. */
+ int vscale = 1000;
+ int velocityx = -5000;
+ int velocityy = -8000;
+ int positionx = 100;
+ int positiony = 300;
+
+ grub_uint32_t frame_count = 0;
+ grub_uint32_t start_time = grub_get_time_ms ();
+
+ while (grub_checkkey () == -1)
+ {
+ /* If the time limit option is set, then check if it's exceeded. */
+ if (vt_opts->test_time != 0
+ && grub_get_time_ms () >= start_time + vt_opts->test_time * 1000)
+ break;
+
+ int newx = positionx + velocityx / vscale;
+ int newy = positiony + velocityy / vscale;
+
+ /* Check collision w/ left */
+ if (newx < view.x && velocityx < 0)
+ {
+ velocityx = -velocityx;
+ newx = positionx + velocityx / vscale;
+ }
+ /* Check collision w/ right */
+ if (newx + bm1width > view.x + view.width && velocityx > 0)
+ {
+ velocityx = -velocityx;
+ newx = positionx + velocityx / vscale;
+ }
+ /* Check collision w/ top */
+ if (newy < 0 && velocityy < 0)
+ {
+ velocityy = -velocityy;
+ newy = positiony + velocityy / vscale;
+ }
+ /* Check collision w/ bottom */
+ if (newy + bm1height > view.y + view.height && velocityy > 0)
+ {
+ velocityy = -velocityy;
+ newy = positiony + velocityy / vscale;
+ }
+
+ positionx = newx;
+ positiony = newy;
+
+ if (clearbg)
+ grub_video_fill_rect (bgcolor, view.x, view.y, view.width,
+ view.height);
+
+ grub_video_blit_bitmap (bitmap1,
+ GRUB_VIDEO_BLIT_REPLACE,
+ view.x + positionx,
+ view.y + positiony, 0, 0, bm1width, bm1height);
+ grub_video_swap_buffers ();
+ frame_count++;
+
+ /* Acceleration due to gravity...
+ * note that the y coordinate is increasing downward. */
+ velocityy += vscale / 7;
+ }
+
+ /* Calculate average frame rate. */
+ grub_uint32_t duration = grub_get_time_ms () - start_time;
+ grub_uint32_t fps_x10 = 10 * 1000 * frame_count / duration;
+
+ /* Eat the keystroke. */
+ if (grub_checkkey () != -1)
+ grub_getkey ();
+
+ grub_video_bitmap_destroy (bitmap1);
+ grub_video_restore ();
+
+ grub_printf ("Average frame rate: %d.%d fps\n", fps_x10 / 10, fps_x10 % 10);
+
+ grub_errno = GRUB_ERR_NONE;
+}
+
+
+
+/* Configuration settings for a benchmark run. */
+struct benchmark_config
+{
+ int width;
+ int height;
+ unsigned int mode_type;
+ int use_rgba_bitmaps;
+};
+
+/* Macro to make the benchmark_configs[] declaration more concise. */
+#define MODE_TYPE_RGB(bpp) (GRUB_VIDEO_MODE_TYPE_RGB \
+ | ((bpp) << GRUB_VIDEO_MODE_TYPE_DEPTH_POS))
+
+/* The video configurations to use for the benchmark. */
+static struct benchmark_config benchmark_configs[] = {
+ {320, 200, MODE_TYPE_RGB (0), 0},
+ {640, 480, MODE_TYPE_RGB (0), 0},
+ {1024, 768, MODE_TYPE_RGB (0), 0},
+ {1024, 768, MODE_TYPE_RGB (0), 1},
+};
+
+#define NUM_BENCHMARK_CONFIGS (sizeof(benchmark_configs) \
+ / sizeof(benchmark_configs[0]))
+
+struct benchmark_result
+{
+ /* If set to 1, the test was able to run successfully;
+ * 0 means there was an error. */
+ int test_passed;
+
+ /* Bits per pixel for the video mode used. */
+ int bpp;
+
+ /* All fps are in fps * 10 to achieve one decimal place. */
+ /* Set to 0 to indicate the test could not be run. */
+ grub_int32_t fill_fps;
+ grub_int32_t blit_fps;
+ grub_int32_t blend_fps;
+};
+
+#define BENCHMARK_RESULT_FPS_SCALE 10
+
+static void
+move_rectangle_one_step (int *x, int *y,
+ int width, int height,
+ int *vx, int *vy, const grub_video_rect_t * bounds)
+{
+ int newx = *x + *vx;
+ int newy = *y + *vy;
+
+ /* Check collision w/ left */
+ if (newx < bounds->x && *vx < 0)
+ {
+ *vx = -*vx;
+ newx = *x + *vx;
+ }
+ /* Check collision w/ right */
+ if (newx + width > bounds->x + bounds->width && *vx > 0)
+ {
+ *vx = -*vx;
+ newx = *x + *vx;
+ }
+ /* Check collision w/ top */
+ if (newy < 0 && *vy < 0)
+ {
+ *vy = -*vy;
+ newy = *y + *vy;
+ }
+ /* Check collision w/ bottom */
+ if (newy + height > bounds->y + bounds->height && *vy > 0)
+ {
+ *vy = -*vy;
+ newy = *y + *vy;
+ }
+
+ *x = newx;
+ *y = newy;
+}
+
+/**
+ * Run the benchmark test for a particular video mode, which is specified
+ * by ``*config``. The results of the test are stored in ``*result``.
+ */
+static void
+do_benchmark (const struct benchmark_config *config,
+ struct benchmark_result *result,
+ struct videotest_options *vt_opts)
+{
+ struct grub_video_mode_info modeinfo;
+
+ result->test_passed = 0;
+ result->fill_fps = 0;
+ result->blit_fps = 0;
+ result->blend_fps = 0;
+ result->bpp = 0;
+
+ int mode_type = config->mode_type;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+ if (grub_video_setup (config->width, config->height,
+ mode_type) != GRUB_ERR_NONE)
+ return;
+
+ if (grub_video_get_info (&modeinfo) == GRUB_ERR_NONE)
+ result->bpp = modeinfo.bpp;
+
+ /* Full screen bitmap blit test. */
+
+ grub_video_rect_t view;
+ grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y,
+ (unsigned *) &view.width,
+ (unsigned *) &view.height);
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+
+ /* For measuring timing. */
+ grub_uint32_t frame_count;
+ grub_uint64_t start_time;
+ grub_uint64_t desired_stop_time;
+ grub_uint32_t duration;
+
+
+ /*** FILL TEST ***/
+
+ /* Alternates between 0 and 1 to change the color. */
+ int color_flag = 0;
+ grub_video_color_t fillcolors[2];
+ fillcolors[0] = grub_video_map_rgb (0, 0, 255);
+ fillcolors[1] = grub_video_map_rgb (255, 255, 0);
+
+ frame_count = 0;
+ start_time = grub_get_time_ms ();
+ desired_stop_time = start_time + vt_opts->test_time * 1000;
+
+ while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1)
+ {
+ grub_video_fill_rect (fillcolors[color_flag],
+ view.x, view.y, view.width, view.height);
+ grub_video_swap_buffers ();
+ color_flag ^= 1;
+ frame_count++;
+ }
+ if (grub_checkkey () != -1)
+ grub_getkey (); /* Eat the keypress, if there was one. */
+
+ /* Calculate average frame rate. */
+ duration = grub_get_time_ms () - start_time;
+ if (duration == 0)
+ {
+ result->fill_fps = 0;
+ }
+ else
+ {
+ result->fill_fps =
+ (BENCHMARK_RESULT_FPS_SCALE * 1000
+ * frame_count / duration);
+ }
+
+
+ /*** BLIT TEST ***/
+
+ /* Generate two bitmaps, the same size as the screen. */
+ const int bitmapwidth = view.width, bitmapheight = view.height;
+ struct grub_video_bitmap *bitmap1;
+ struct grub_video_bitmap *bitmap2;
+ enum grub_video_blit_format bitmap_format = config->use_rgba_bitmaps
+ ? GRUB_VIDEO_BLIT_FORMAT_RGBA_8888 : GRUB_VIDEO_BLIT_FORMAT_RGB_888;
+
+ if (grub_video_bitmap_create (&bitmap1, bitmapwidth, bitmapheight,
+ bitmap_format) != GRUB_ERR_NONE)
+ return;
+
+ if (grub_video_bitmap_create (&bitmap2, bitmapwidth, bitmapheight,
+ bitmap_format) != GRUB_ERR_NONE)
+ {
+ grub_video_bitmap_destroy (bitmap1);
+ return;
+ }
+
+ int offset;
+ int x;
+ int y;
+ grub_uint8_t *data;
+
+ offset = 0;
+ data = grub_video_bitmap_get_data (bitmap1);
+ for (y = 0; y < bitmapheight; y++)
+ {
+ for (x = 0; x < bitmapwidth; x++)
+ {
+ data[offset++] = x ^ y; /* red */
+ data[offset++] = (x * 3) ^ (y * 3); /* green */
+ data[offset++] = (x * 2) ^ (y * 2); /* blue */
+ if (config->use_rgba_bitmaps)
+ data[offset++] = 255;
+ }
+ }
+
+ offset = 0;
+ data = grub_video_bitmap_get_data (bitmap2);
+ for (y = 0; y < bitmapheight; y++)
+ {
+ for (x = 0; x < bitmapwidth; x++)
+ {
+ data[offset++] = x + y; /* red */
+ data[offset++] = x * x + y * y; /* green */
+ data[offset++] = x * x / 4 + y * y / 4; /* blue */
+ if (config->use_rgba_bitmaps)
+ data[offset++] = 255;
+ }
+ }
+
+
+ /* Now do the blit test, alternating between the two bitmaps. */
+ frame_count = 0;
+ start_time = grub_get_time_ms ();
+ desired_stop_time = start_time + vt_opts->test_time * 1000;
+
+ int cur = 0; /* Which bitmap to draw this frame. */
+ while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1)
+ {
+ struct grub_video_bitmap *current_bitmap = cur == 0 ? bitmap1 : bitmap2;
+ grub_video_blit_bitmap (current_bitmap,
+ GRUB_VIDEO_BLIT_REPLACE,
+ view.x, view.y, 0, 0, bitmapwidth,
+ bitmapheight);
+ grub_video_swap_buffers ();
+ frame_count++;
+ cur ^= 1;
+ }
+ if (grub_checkkey () != -1)
+ grub_getkey (); /* Eat the keypress, if there was once. */
+
+ /* Calculate average frame rate. */
+ duration = grub_get_time_ms () - start_time;
+ if (duration == 0)
+ {
+ result->blit_fps = 0;
+ }
+ else
+ {
+ result->blit_fps =
+ (BENCHMARK_RESULT_FPS_SCALE * 1000
+ * frame_count / duration);
+ }
+
+ grub_video_bitmap_destroy (bitmap2);
+ grub_video_bitmap_destroy (bitmap1);
+
+
+
+ /*** BLEND TEST ***/
+
+ /* Generate two bitmaps, with alpha translucency. */
+ const int bbw = view.width * 2 / 3;
+ const int bbh = view.height * 2 / 3;
+
+ if (grub_video_bitmap_create (&bitmap1, bbw, bbh,
+ GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) !=
+ GRUB_ERR_NONE)
+ return;
+
+ if (grub_video_bitmap_create (&bitmap2, bbw, bbh,
+ GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) !=
+ GRUB_ERR_NONE)
+ {
+ grub_video_bitmap_destroy (bitmap1);
+ return;
+ }
+
+ offset = 0;
+ data = grub_video_bitmap_get_data (bitmap1);
+ for (y = 0; y < bbh; y++)
+ {
+ for (x = 0; x < bbw; x++)
+ {
+ /* Calculate a to be increasing away from the center. */
+ int dx = 256 * (x - bbw / 2) / (bbw / 2);
+ int dy = 256 * (y - bbh / 2) / (bbh / 2);
+ int a = dx * dx + dy * dy;
+ /* range for a = 0 .. 2*(256^2) = 2*2^16 = 2^17 */
+ a >>= 17 - 8; /* Make range 0..256. */
+ if (a > 255)
+ a = 255;
+
+ data[offset++] = x ^ y; /* red */
+ data[offset++] = (x * 3) ^ (y * 3); /* green */
+ data[offset++] = (x * 2) ^ (y * 2); /* blue */
+ data[offset++] = 255 - a;
+ }
+ }
+
+ offset = 0;
+ data = grub_video_bitmap_get_data (bitmap2);
+ for (y = 0; y < bbh; y++)
+ {
+ for (x = 0; x < bbw; x++)
+ {
+ data[offset++] = x + y; /* red */
+ data[offset++] = x * x + y * y; /* green */
+ data[offset++] = x * x / 4 + y * y / 4; /* blue */
+ data[offset++] = 255;
+ }
+ }
+
+ frame_count = 0;
+ start_time = grub_get_time_ms ();
+ desired_stop_time = start_time + vt_opts->test_time * 1000;
+
+
+ grub_video_color_t bgcolor = grub_video_map_rgb (80, 80, 80);
+
+ /* Bitmap locations. */
+ int b1x = 0;
+ int b1y = 0;
+ int b2x = view.width - bbw;
+ int b2y = 0;
+ /* Bitmap velocities. */
+ int b1vx = 8;
+ int b1vy = 12;
+ int b2vx = -10;
+ int b2vy = 9;
+
+ while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1)
+ {
+ move_rectangle_one_step (&b1x, &b1y, bbw, bbh, &b1vx, &b1vy, &view);
+ move_rectangle_one_step (&b2x, &b2y, bbw, bbh, &b2vx, &b2vy, &view);
+ grub_video_fill_rect (bgcolor, view.x, view.y, view.width, view.height);
+ grub_video_blit_bitmap (bitmap2,
+ GRUB_VIDEO_BLIT_BLEND,
+ b2x, b2y, 0, 0, bbw, bbh);
+ grub_video_blit_bitmap (bitmap1,
+ GRUB_VIDEO_BLIT_BLEND,
+ b1x, b1y, 0, 0, bbw, bbh);
+ grub_video_swap_buffers ();
+ frame_count++;
+ cur ^= 1;
+ }
+ if (grub_checkkey () != -1)
+ grub_getkey (); /* Eat the keypress, if there was once. */
+
+ /* Calculate average frame rate. */
+ duration = grub_get_time_ms () - start_time;
+ if (duration == 0)
+ {
+ result->blend_fps = 0;
+ }
+ else
+ {
+ result->blend_fps =
+ (BENCHMARK_RESULT_FPS_SCALE * 1000
+ * frame_count / duration);
+ }
+
+ grub_video_bitmap_destroy (bitmap2);
+ grub_video_bitmap_destroy (bitmap1);
+
+ grub_video_restore ();
+ result->test_passed = 1;
+}
+
+/**
+ * Run a benchmark test in a series of video modes.
+ * The results are reported in tabular form. This will be helpful to
+ * determine how effective various optimizations are.
+ */
+static void
+benchmark_test (struct videotest_options *vt_opts)
+{
+ unsigned int i;
+ struct benchmark_result results[NUM_BENCHMARK_CONFIGS];
+
+ /* Set option default values. */
+ if (vt_opts->test_time == 0)
+ vt_opts->test_time = DEFAULT_TEST_TIME;
+
+ /* Run benchmarks. */
+ for (i = 0; i < NUM_BENCHMARK_CONFIGS; i++)
+ {
+ grub_error_push ();
+ do_benchmark (&benchmark_configs[i], &results[i], vt_opts);
+ }
+
+ grub_print_error ();
+
+ /* Display results. */
+ grub_printf ("Benchmark results (in frames/s):\n");
+ grub_printf ("(W=Width, H=Height, B=Bits per pixel,\n"
+ " T=Mode Type, A=Bitmap Alpha)\n");
+ grub_printf (" W H B T A FILL BLIT BLEND\n");
+ for (i = 0; i < NUM_BENCHMARK_CONFIGS; i++)
+ {
+ struct benchmark_config *c = &benchmark_configs[i];
+ struct benchmark_result *r = &results[i];
+
+ if (r->test_passed)
+ {
+ grub_printf ("%4dx%4d %2d %d %d: %4d.%d %4d.%d %4d.%d\n",
+ c->width, c->height, r->bpp,
+ c->mode_type & 0x0F,
+ c->use_rgba_bitmaps,
+ r->fill_fps / BENCHMARK_RESULT_FPS_SCALE,
+ r->fill_fps % BENCHMARK_RESULT_FPS_SCALE,
+ r->blit_fps / BENCHMARK_RESULT_FPS_SCALE,
+ r->blit_fps % BENCHMARK_RESULT_FPS_SCALE,
+ r->blend_fps / BENCHMARK_RESULT_FPS_SCALE,
+ r->blend_fps % BENCHMARK_RESULT_FPS_SCALE);
+ }
+ else
+ {
+ grub_printf ("%4dx%4d %2d %d Not supported.\n",
+ c->width, c->height,
+ ((c->mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
+ >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS),
+ c->mode_type & 0x0F);
+ }
+ }
+
+ grub_errno = GRUB_ERR_NONE;
+}
+
+
+/**
+ * Test time functions.
+ */
+static void
+clock_test (struct videotest_options *vt_opts)
+{
+ int mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+ if (grub_video_setup (640, 480, mode_type) != GRUB_ERR_NONE)
+ return;
+
+ grub_video_rect_t view;
+ grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y,
+ (unsigned *) &view.width,
+ (unsigned *) &view.height);
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+ /* Draw a progress bar that animates in sync with time. */
+
+ grub_video_rect_t bar_frame;
+ bar_frame.width = view.width - 2 * view.width / 10;
+ bar_frame.height = view.height / 20;
+ bar_frame.x = view.x + view.width / 10;
+ bar_frame.y = view.y + view.height - bar_frame.height - view.height / 10;
+
+ grub_video_color_t bgcolor = grub_video_map_rgb (50, 50, 50);
+ grub_video_color_t framecolor = grub_video_map_rgb (255, 255, 255);
+ grub_video_color_t barbgcolor = grub_video_map_rgb (0, 0, 128);
+ grub_video_color_t barcolor = grub_video_map_rgb (100, 100, 255);
+
+ grub_uint32_t frame_count;
+ grub_uint64_t start_time;
+ grub_uint64_t barstart;
+ grub_uint32_t barlength = 1000;
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+ frame_count = 0;
+ start_time = grub_get_time_ms ();
+ barstart = grub_get_time_ms ();
+
+ while (grub_checkkey () == -1)
+ {
+ grub_uint64_t now;
+ grub_uint32_t bartime;
+ now = grub_get_time_ms ();
+ /* If the time limit option is set, then check if it's exceeded. */
+ if (vt_opts->test_time != 0
+ && now >= start_time + vt_opts->test_time * 1000)
+ break;
+ bartime = now - barstart;
+ if (bartime > barlength)
+ {
+ barstart = grub_get_time_ms (); /* Start over. */
+ bartime = barlength;
+ }
+
+ /* Clear screen. */
+ grub_video_fill_rect (bgcolor, view.x, view.y, view.width, view.height);
+
+ /* Border. */
+ grub_video_fill_rect (framecolor,
+ bar_frame.x - 1, bar_frame.y - 1,
+ bar_frame.width + 2, bar_frame.height + 2);
+
+ /* Bar background. */
+ int barwidth = bar_frame.width * bartime / barlength;
+ grub_video_fill_rect (barbgcolor, bar_frame.x + barwidth,
+ bar_frame.y, bar_frame.width - barwidth,
+ bar_frame.height);
+ /* Bar foreground. */
+ grub_video_fill_rect (barcolor, bar_frame.x, bar_frame.y,
+ barwidth, bar_frame.height);
+ grub_video_swap_buffers ();
+ frame_count++;
+ }
+
+ grub_uint32_t duration = grub_get_time_ms () - start_time;
+ grub_uint32_t fps_x10 = 10 * 1000 * frame_count / duration;
+
+ if (grub_checkkey () != -1)
+ grub_getkey (); /* Eat the keypress, if there was one. */
+ grub_video_restore ();
+ grub_printf ("Average frame rate: %d.%d fps\n", fps_x10 / 10, fps_x10 % 10);
+}
+
+
+/**
+ * Test double buffering.
+ */
+static void
+doublebuf_test (struct videotest_options *vt_opts)
+{
+ int mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+ if (grub_video_setup (640, 480, mode_type) != GRUB_ERR_NONE)
+ return;
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+ grub_video_color_t red = grub_video_map_rgb (255, 50, 50);
+ grub_video_color_t yellow = grub_video_map_rgb (255, 255, 0);
+ grub_video_color_t green = grub_video_map_rgb (20, 255, 20);
+ grub_video_color_t blue = grub_video_map_rgb (50, 50, 255);
+ grub_video_color_t black = grub_video_map_rgb (0, 0, 0);
+ grub_video_color_t bgcolor = grub_video_map_rgb (255, 255, 255);
+
+ grub_video_fill_rect (bgcolor, 0, 0, 640, 480);
+ grub_video_fill_rect (red, 100, 100, 200, 200);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ grub_video_fill_rect (bgcolor, 0, 0, 640, 480);
+ grub_video_fill_rect (yellow, 120, 120, 200, 200);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ grub_video_fill_rect (bgcolor, 0, 0, 640, 480);
+ grub_video_fill_rect (green, 140, 140, 200, 200);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ grub_video_fill_rect (bgcolor, 0, 0, 640, 480);
+ grub_video_fill_rect (blue, 160, 160, 200, 200);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ grub_video_fill_rect (bgcolor, 0, 0, 640, 480);
+ grub_video_fill_rect (black, 180, 180, 200, 200);
+ grub_video_swap_buffers ();
+ grub_getkey ();
+
+ grub_video_restore ();
+}
+
+
+/**
+ * Test text rendering.
+ */
+static void
+text_test (struct videotest_options *vt_opts)
+{
+ grub_video_color_t color;
+ const char *s;
+ int view_x;
+ int view_y;
+ int view_width;
+ int view_height;
+ int xpos;
+ int ypos;
+ int i;
+ grub_font_t font1;
+ grub_font_t font2;
+ grub_font_t font3;
+
+ int mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+ if (grub_video_setup (1024, 768, mode_type) != GRUB_ERR_NONE)
+ return;
+
+ grub_video_get_viewport ((unsigned int *) &view_x,
+ (unsigned int *) &view_y,
+ (unsigned int *) &view_width,
+ (unsigned int *) &view_height);
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+ if (!(font1 = grub_font_get ("New Century Schoolbook 24"))
+ || !(font2 = grub_font_get ("Helvetica Bold 14"))
+ || !(font3 = grub_font_get ("Helvetica 10")))
+ {
+ grub_video_restore ();
+ grub_error (GRUB_ERR_BAD_FONT, "No font loaded.");
+ return;
+ }
+
+ color = grub_video_map_rgb (0, 0, 0);
+ grub_video_fill_rect (color, 0, 0, view_width, view_height);
+
+ color = grub_video_map_rgb (255, 0, 0);
+ grub_video_fill_rect (color, 0, 0, 100, 100);
+
+ color = grub_video_map_rgb (0, 255, 255);
+ grub_video_fill_rect (color, 100, 100, 100, 100);
+
+ color = grub_video_map_rgb (255, 255, 255);
+
+ xpos = 10;
+ ypos = 30;
+ s = "Hello, World!";
+ for (i = 0; i < 40; i++)
+ {
+ if (xpos + grub_font_get_string_width (font1, s) >= view_width)
+ {
+ /* The string will wrap; go to the beginning of the next line. */
+ xpos = 10;
+ ypos += (grub_font_get_descent (font1)
+ + grub_font_get_ascent (font1));
+ }
+ grub_video_draw_string ("Hello, World!",
+ font1, grub_video_map_rgb (255, 255, 0),
+ view_x + xpos, view_y + ypos);
+
+ xpos += grub_font_get_string_width (font1, s);
+ }
+
+ xpos = 300;
+ ypos = 450;
+ grub_video_draw_string (grub_font_get_name (font1),
+ font1, grub_video_map_rgb (255, 255, 255),
+ view_x + xpos, view_y + ypos);
+ ypos += grub_font_get_descent (font1) + grub_font_get_ascent (font2) + 2;
+ grub_video_draw_string (grub_font_get_name (font2),
+ font2, grub_video_map_rgb (255, 255, 255),
+ view_x + xpos, view_y + ypos);
+ ypos += grub_font_get_descent (font2) + grub_font_get_ascent (font3) + 2;
+ grub_video_draw_string (grub_font_get_name (font3),
+ font3, grub_video_map_rgb (255, 255, 255),
+ view_x + xpos, view_y + ypos);
+
+ grub_video_swap_buffers ();
+ grub_getkey ();
+ grub_video_restore ();
+ grub_errno = GRUB_ERR_NONE;
+}
+
+
+/**
+ * Test bitmap scaling.
+ */
+static void
+scale_test (struct videotest_options *vt_opts)
+{
+ int mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
+ if (vt_opts->double_buffering)
+ mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+ if (grub_video_setup (1024, 768, mode_type) != GRUB_ERR_NONE)
+ return;
+
+ grub_errno = GRUB_ERR_NONE;
+ grub_video_rect_t view;
+ grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y,
+ (unsigned *) &view.width,
+ (unsigned *) &view.height);
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+ grub_font_t font;
+ if (!(font = grub_font_get ("Helvetica 10")))
+ {
+ grub_video_restore ();
+ grub_error (GRUB_ERR_BAD_FONT, "No font loaded.");
+ return;
+ }
+
+ grub_video_color_t color;
+
+ int text_y = 0;
+ int text_height = 25;
+
+ color = grub_video_map_rgb (44, 44, 200);
+ grub_video_fill_rect (color, view.x, view.y, view.width, view.height);
+ color = grub_video_map_rgb (255, 255, 255);
+
+ grub_video_draw_string ("Loading image",
+ font, color, 10, text_y += text_height);
+
+ enum grub_video_bitmap_scale_method scale_method =
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST;
+ const char *bitmap_name = "/boot/grub/themes/winter/without-leaves.png";
+ struct grub_video_bitmap *bitmap;
+ grub_video_bitmap_load (&bitmap, bitmap_name);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_video_draw_string ("Error loading bitmap",
+ font, color, 10, text_y += text_height);
+ }
+ else
+ {
+ grub_video_draw_string ("Original image",
+ font, color, 10, text_y += text_height);
+ grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND,
+ 400, text_y - 10,
+ 0, 0, grub_video_bitmap_get_width (bitmap),
+ grub_video_bitmap_get_height (bitmap));
+
+ struct grub_video_bitmap *bitmap2;
+ if (grub_video_bitmap_create_scaled (&bitmap2, 40, 40,
+ bitmap,
+ scale_method)
+ != GRUB_ERR_NONE)
+ {
+ grub_video_draw_string ("Error scaling down bitmap",
+ font, color, 10, text_y += text_height);
+ }
+ else
+ {
+ grub_video_draw_string ("Scaled down version",
+ font, color, 10, text_y += text_height);
+ grub_video_blit_bitmap (bitmap2, GRUB_VIDEO_BLIT_BLEND,
+ 400, text_y + 100,
+ 0, 0, grub_video_bitmap_get_width (bitmap2),
+ grub_video_bitmap_get_height (bitmap2));
+ grub_video_bitmap_destroy (bitmap2);
+ }
+
+ struct grub_video_bitmap *bitmap3;
+ if (grub_video_bitmap_create_scaled (&bitmap3, 500, 300, bitmap,
+ scale_method)
+ != GRUB_ERR_NONE)
+ {
+ grub_video_draw_string ("Error scaling up bitmap",
+ font, color, 10, text_y += text_height);
+ }
+ else
+ {
+ grub_video_draw_string ("Scaled up version",
+ font, color, 10, text_y += text_height);
+ grub_video_blit_bitmap (bitmap3, GRUB_VIDEO_BLIT_BLEND,
+ 400, text_y + 50,
+ 0, 0, grub_video_bitmap_get_width (bitmap3),
+ grub_video_bitmap_get_height (bitmap3));
+ grub_video_bitmap_destroy (bitmap3);
+ }
+ }
+
+ grub_video_swap_buffers ();
+ grub_getkey ();
+ grub_video_bitmap_destroy (bitmap);
+ grub_video_restore ();
+}
+
+
+/** Print a list of the available tests. */
+static void list_tests (void);
+
+/**
+ * Video test command. Takes an argument specifying the test to run.
+ */
+static grub_err_t
+grub_cmd_videotest (struct grub_arg_list *state, int argc, char **args)
+{
+ int i;
+ struct videotest_options vt_opts;
+ /* Pointer to the test function. */
+ void (*test_func) (struct videotest_options *) = NULL;
+
+ vt_opts.test_time =
+ state[ARGINDEX_TEST_TIME].set
+ ? grub_strtoul (state[ARGINDEX_TEST_TIME].arg, 0, 0) : 0;
+ vt_opts.double_buffering = state[ARGINDEX_DOUBLE_BUF].set;
+
+ /* Parse command line arguments to determine the test to run. */
+ for (i = 0; i < argc; i++)
+ {
+ char *arg = args[i];
+ if (grub_strcmp (arg, "list") == 0)
+ {
+ list_tests ();
+ return GRUB_ERR_NONE;
+ }
+ else if (grub_strcmp (arg, "basic") == 0)
+ {
+ test_func = basic_video_test;
+ }
+ else if (grub_strcmp (arg, "bench") == 0)
+ {
+ test_func = benchmark_test;
+ }
+ else if (grub_strcmp (arg, "bitmaps") == 0)
+ {
+ test_func = bitmap_demo;
+ }
+ else if (grub_strcmp (arg, "clock") == 0)
+ {
+ test_func = clock_test;
+ }
+ else if (grub_strcmp (arg, "doublebuf") == 0)
+ {
+ test_func = doublebuf_test;
+ }
+ else if (grub_strcmp (arg, "text") == 0)
+ {
+ test_func = text_test;
+ }
+ else if (grub_strcmp (arg, "scale") == 0)
+ {
+ test_func = scale_test;
+ }
+ else
+ {
+ grub_printf ("Error: Unknown test `%s'\n", arg);
+ grub_errno = GRUB_ERR_BAD_ARGUMENT;
+ return grub_errno;
+ }
+ }
+
+ if (test_func == NULL)
+ {
+ grub_printf ("Usage: videotest TESTNAME Run a test.\n");
+ grub_printf (" videotest list List available tests.\n");
+ grub_printf ("\n");
+ list_tests ();
+ }
+ else
+ {
+ test_func (&vt_opts);
+ }
+
return grub_errno;
}
-GRUB_MOD_INIT(videotest)
+static void
+list_tests (void)
+{
+ grub_printf ("Available tests\n");
+ grub_printf ("===============\n");
+ grub_printf ("basic Basic video test with filled rectangles,\n");
+ grub_printf (" offscreen rendering targets, some text.\n");
+ grub_printf ("bench Run a performance benchmark.\n");
+ grub_printf ("bitmaps Test generating and blitting bitmaps.\n");
+ grub_printf ("clock Test time functions w/ animated progress bar.\n");
+ grub_printf ("doublebuf Test double buffering.\n");
+ grub_printf ("text Test text rendering.\n");
+ grub_printf ("scale Test image scaling.\n");
+ grub_printf ("\n");
+}
+
+
+GRUB_MOD_INIT (videotest)
{
grub_register_command ("videotest",
grub_cmd_videotest,
GRUB_COMMAND_FLAG_BOTH,
- "videotest",
- "Test video subsystem",
- 0);
+ "videotest TEST",
+ "Run the specified video subsystem test.",
+ arg_options);
}
-GRUB_MOD_FINI(videotest)
+GRUB_MOD_FINI (videotest)
{
grub_unregister_command ("videotest");
}
=== modified file 'include/grub/video.h'
--- include/grub/video.h 2008-09-01 16:09:34 +0000
+++ include/grub/video.h 2008-09-01 17:33:56 +0000
@@ -151,6 +151,16 @@
grub_uint8_t a; /* Reserved bits value (0-255). */
};
+/* A 2D rectangle type. */
+struct grub_video_rect
+{
+ int x;
+ int y;
+ int width;
+ int height;
+};
+typedef struct grub_video_rect grub_video_rect_t;
+
struct grub_video_adapter
{
/* The video adapter name. */