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. */