From 05c60bac51f57d8a327dc56470bf89144e823a09 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Thu, 8 Apr 2021 23:05:27 +0200 Subject: [PATCH] gterm: Speed up terminal greatly by caching an alpha blent canvas of the background instead of alpha blending every pixel of the background every time --- CONFIG.md | 4 +- stage23/drivers/vga_textmode.s2.c | 2 +- stage23/entry.s3.c | 4 +- stage23/lib/gterm.c | 97 +++++++++++++------------------ stage23/lib/term.s2.c | 4 +- stage23/menu.c | 6 +- test/limine.cfg | 2 +- 7 files changed, 52 insertions(+), 67 deletions(-) diff --git a/CONFIG.md b/CONFIG.md index 131f7571..fc13600b 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -51,11 +51,11 @@ Some keys take *URIs* as values; these are described in the next section. *Globally assignable* keys are: * `TIMEOUT` - Specifies the timeout in seconds before the first *entry* is automatically booted. If set to `no`, disable automatic boot. If set to `0`, boots default entry instantly (see `DEFAULT_ENTRY` key). * `DEFAULT_ENTRY` - 1-based entry index of the entry which will be automatically selected at startup. If unspecified, it is `1`. -* `GRAPHICS` - If set to `yes`, do use graphical VESA framebuffer for the boot menu, else use text mode. +* `GRAPHICS` - If set to `yes`, do use graphical VESA framebuffer for the boot menu, else use text mode. Ignored with Limine UEFI. * `MENU_RESOLUTION` - Specify screen resolution to be used by the Limine menu in the form `x`. This will *only* affect the menu, not any booted OS. If not specified, Limine will pick a resolution automatically. If the resolution is not available, Limine will pick another one automatically. Ignored if `GRAPHICS` is not `yes`. * `MENU_BRANDING` - A string that will be displayed on top of the Limine menu. * `MENU_FONT` - URI path to a font file to be used instead of the default one for the menu. The font file must be a code page 437 character set comprised of 256 consecutive 8x16 glyphs bitmaps (4096 byte font file). Each glyph's bitmap must be expressed left to right (1 byte per row), and top to bottom (16 bytes per whole glyph). -* `THEME_COLOURS` - Specifies the colour palette used by the terminal (AARRGGBB). It is a `;` separated array of 8 colours: black, red, green, brown, blue, magenta, cyan, and gray, respectively. Ignored if `GRAPHICS` is not `yes`. +* `THEME_COLOURS` - Specifies the colour palette used by the terminal (AARRGGBB). It is a `;` separated array of 10 colours: black, red, green, brown, blue, magenta, cyan, gray, background, and foreground respectively. While an alpha transparency value can be specified for every colour, it is ignored for all but background. Ignored if `GRAPHICS` is not `yes`. * `THEME_COLORS` - Alias of `THEME_COLOURS`. * `THEME_MARGIN` - Set the amount of margin around the terminal. Ignored if `GRAPHICS` is not `yes`. * `THEME_MARGIN_GRADIENT` - Set the thickness in pixel for the gradient around the terminal. Ignored if `GRAPHICS` is not `yes`. diff --git a/stage23/drivers/vga_textmode.s2.c b/stage23/drivers/vga_textmode.s2.c index c36b4d83..a359961d 100644 --- a/stage23/drivers/vga_textmode.s2.c +++ b/stage23/drivers/vga_textmode.s2.c @@ -157,7 +157,7 @@ void text_set_cursor_pos(int x, int y) { draw_cursor(); } -static uint8_t ansi_colours[] = { 0, 4, 2, 0x0e, 1, 5, 3, 7 }; +static uint8_t ansi_colours[] = { 0, 4, 2, 0x0e, 1, 5, 3, 7, 0, 7 }; void text_set_text_fg(int fg) { text_palette = (text_palette & 0xf0) | ansi_colours[fg]; diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c index aeb37802..7df046a6 100644 --- a/stage23/entry.s3.c +++ b/stage23/entry.s3.c @@ -40,7 +40,9 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable 0x000000aa, // blue 0x00aa00aa, // magenta 0x0000aaaa, // cyan - 0x00aaaaaa // grey + 0x00aaaaaa, // grey + 0x00000000, // background (black) + 0x00aaaaaa // foreground (white) }; term_vbe(colourscheme, 64, 0, NULL); diff --git a/stage23/lib/gterm.c b/stage23/lib/gterm.c index 3a03acff..cbf4fe7b 100644 --- a/stage23/lib/gterm.c +++ b/stage23/lib/gterm.c @@ -26,7 +26,7 @@ extern symbol _binary_font_bin_start; static uint8_t *vga_font = NULL; -static uint32_t ansi_colours[8]; +static uint32_t ansi_colours[10]; static int frame_height, frame_width; @@ -35,6 +35,8 @@ static struct image *background; static struct gterm_char *grid = NULL; static struct gterm_char *front_grid = NULL; +static uint32_t *bg_canvas = NULL; + static bool double_buffer_enabled = false; static bool cursor_status = true; @@ -42,11 +44,6 @@ static bool cursor_status = true; static int cursor_x; static int cursor_y; -static uint32_t cursor_fg = 0x00000000; -static uint32_t cursor_bg = 0x00ffffff; -static uint32_t text_fg; -static uint32_t text_bg; - static int rows; static int cols; static int margin_gradient; @@ -74,16 +71,10 @@ void gterm_plot_px(int x, int y, uint32_t hex) { gterm_framebuffer[fb_i] = hex; } -static void _gterm_plot_bg_blent_px(int x, int y, uint32_t hex) { - gterm_plot_px(x, y, colour_blend(hex, background->get_pixel(background, x, y))); -} - -void (*gterm_plot_bg_blent_px)(int x, int y, uint32_t hex) = gterm_plot_px; - static uint32_t blend_gradient_from_box(int x, int y, uint32_t hex) { if (x >= frame_width && x < frame_width + VGA_FONT_WIDTH * cols && y >= frame_height && y < frame_height + VGA_FONT_HEIGHT * rows) { - return hex; + return colour_blend(hex, background->get_pixel(background, x, y)); } uint32_t bg_px = background->get_pixel(background, x, y); @@ -121,53 +112,43 @@ static uint32_t blend_gradient_from_box(int x, int y, uint32_t hex) { return colour_blend((hex & 0xffffff) | (new_alpha << 24), bg_px); } -void gterm_plot_background(int x, int y, int width, int height) { +void gterm_generate_canvas(void) { if (background) { - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - gterm_plot_px(x + xx, y + yy, blend_gradient_from_box(xx, yy, text_bg)); + for (int y = 0; y < gterm_height; y++) { + for (int x = 0; x < gterm_width; x++) { + bg_canvas[y * gterm_width + x] = blend_gradient_from_box(x, y, ansi_colours[8]); + gterm_plot_px(x, y, bg_canvas[y * gterm_width + x]); } } } else { - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - gterm_plot_px(x + xx, y + yy, text_bg); + for (int y = 0; y < gterm_height; y++) { + for (int x = 0; x < gterm_width; x++) { + bg_canvas[y * gterm_width + x] = ansi_colours[8]; + gterm_plot_px(x, y, ansi_colours[8]); } } } } -void gterm_plot_rect(int x, int y, int width, int height, uint32_t hex) { - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - gterm_plot_px(x + xx, y + yy, hex); - } - } -} - -void gterm_plot_bg_blent_rect(int x, int y, int width, int height, uint32_t hex) { - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - gterm_plot_bg_blent_px(x + xx, y + yy, hex); - } - } -} - struct gterm_char { uint32_t c; - uint32_t fg; - uint32_t bg; + int fg; + int bg; }; void gterm_plot_char(struct gterm_char *c, int x, int y) { uint8_t *glyph = &vga_font[(size_t)c->c * VGA_FONT_HEIGHT]; - gterm_plot_bg_blent_rect(x, y, VGA_FONT_WIDTH, VGA_FONT_HEIGHT, c->bg); - for (int i = 0; i < VGA_FONT_HEIGHT; i++) { for (int j = 0; j < VGA_FONT_WIDTH; j++) { - if ((glyph[i] & (0x80 >> j))) - gterm_plot_bg_blent_px(x + j, y + i, c->fg); + if ((glyph[i] & (0x80 >> j))) { + gterm_plot_px(x + j, y + i, ansi_colours[c->fg]); + } else { + if (c->bg == 8) + gterm_plot_px(x + j, y + i, bg_canvas[(y + i) * gterm_width + (x + j)]); + else + gterm_plot_px(x + j, y + i, ansi_colours[c->bg]); + } } } } @@ -182,16 +163,16 @@ static void plot_char_grid(struct gterm_char *c, int x, int y) { static void clear_cursor(void) { struct gterm_char c = grid[cursor_x + cursor_y * cols]; - c.fg = text_fg; - c.bg = text_bg; + c.fg = 9; + c.bg = 8; plot_char_grid(&c, cursor_x, cursor_y); } static void draw_cursor(void) { if (cursor_status) { struct gterm_char c = grid[cursor_x + cursor_y * cols]; - c.fg = cursor_fg; - c.bg = cursor_bg; + c.fg = 0; + c.bg = 7; plot_char_grid(&c, cursor_x, cursor_y); } } @@ -206,8 +187,8 @@ static void scroll(void) { // Clear the last line of the screen. struct gterm_char empty; empty.c = ' '; - empty.fg = text_fg; - empty.bg = text_bg; + empty.fg = 9; + empty.bg = 8; for (int i = rows * cols - cols; i < rows * cols; i++) { plot_char_grid(&empty, i % cols, i / cols); } @@ -220,8 +201,8 @@ void gterm_clear(bool move) { struct gterm_char empty; empty.c = ' '; - empty.fg = text_fg; - empty.bg = text_bg; + empty.fg = 9; + empty.bg = 8; for (int i = 0; i < rows * cols; i++) { plot_char_grid(&empty, i % cols, i / cols); } @@ -256,12 +237,14 @@ void gterm_get_cursor_pos(int *x, int *y) { *y = cursor_y; } +static int text_fg = 9, text_bg = 8; + void gterm_set_text_fg(int fg) { - text_fg = ansi_colours[fg]; + text_fg = fg; } void gterm_set_text_bg(int bg) { - text_bg = ansi_colours[bg]; + text_bg = bg; } void gterm_double_buffer_flush(void) { @@ -395,19 +378,17 @@ bool gterm_init(int *_rows, int *_cols, uint32_t *_colours, int _margin, int _ma front_grid = ext_mem_alloc(rows * cols * sizeof(struct gterm_char)); background = _background; - if (background) - gterm_plot_bg_blent_px = _gterm_plot_bg_blent_px; - memcpy(ansi_colours, _colours, sizeof(ansi_colours)); - text_bg = ansi_colours[0]; - text_fg = ansi_colours[7]; margin_gradient = _margin_gradient; frame_height = gterm_height / 2 - (VGA_FONT_HEIGHT * rows) / 2; frame_width = gterm_width / 2 - (VGA_FONT_WIDTH * cols) / 2; - gterm_plot_background(0, 0, gterm_width, gterm_height); + if (bg_canvas == NULL) + bg_canvas = ext_mem_alloc(gterm_width * gterm_height * sizeof(uint32_t)); + + gterm_generate_canvas(); gterm_clear(true); return true; diff --git a/stage23/lib/term.s2.c b/stage23/lib/term.s2.c index 690144e1..1ff0cb54 100644 --- a/stage23/lib/term.s2.c +++ b/stage23/lib/term.s2.c @@ -101,8 +101,8 @@ static void term_putchar(uint8_t c) { static void sgr(void) { if (esc_value0 == 0){ - set_text_bg(0); - set_text_fg(7); + set_text_bg(8); + set_text_fg(9); return; } diff --git a/stage23/menu.c b/stage23/menu.c index 8dd994e5..6ce388fe 100644 --- a/stage23/menu.c +++ b/stage23/menu.c @@ -379,7 +379,9 @@ char *menu(char **cmdline) { 0x000000aa, // blue 0x00aa00aa, // magenta 0x0000aaaa, // cyan - 0x00aaaaaa // grey + 0x00aaaaaa, // grey + 0x00000000, // background (black) + 0x00aaaaaa // foreground (grey) }; char *colours = config_get_value(NULL, 0, "THEME_COLOURS"); @@ -387,7 +389,7 @@ char *menu(char **cmdline) { colours = config_get_value(NULL, 0, "THEME_COLORS"); if (colours != NULL) { const char *first = colours; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 10; i++) { const char *last; uint32_t col = strtoui(first, &last, 16); if (first == last) diff --git a/test/limine.cfg b/test/limine.cfg index 6f20bdfa..19ab86d6 100644 --- a/test/limine.cfg +++ b/test/limine.cfg @@ -3,7 +3,7 @@ TIMEOUT=3 GRAPHICS=yes MENU_FONT=boot:///boot/font.bin -THEME_COLOURS=60000000;aa0000;00aaff;aa5500;0000aa;aa00aa;9076de;aaaaaa +THEME_COLOURS=000000;aa0000;00aaff;aa5500;0000aa;aa00aa;9076de;aaaaaa;60000000;aaaaaa THEME_MARGIN=64 BACKGROUND_PATH=boot:///boot/bg.bmp