From 0a3831824a4e93825c5b939f406545eead622fbe Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sun, 23 May 2021 17:37:16 +0200 Subject: Simplify and speed up the text engine --- src/main.c | 9 +- src/text.h | 235 +++----------------------------------------------- src/uxn/devices/ppu.c | 29 +++++-- 3 files changed, 35 insertions(+), 238 deletions(-) diff --git a/src/main.c b/src/main.c index bf30628..f612ef7 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,6 @@ #include "common.h" #include "bitmap.h" -#include "bd-font.c" #include "filesystem.c" #include "rom.c" @@ -281,13 +280,7 @@ int main(void) { init_uxn(&u); // Initialize text engine. - txt_init_hybrid( - TXT_MODE_HYBRID, - (Font){ - .data = bd_font, - .char_height = 8, - .char_width = 8, - }, ppu.fg); + txt_init(1, ppu.fg); txt_position(0,0); // Main loop. diff --git a/src/text.h b/src/text.h index c27537f..59ab9af 100644 --- a/src/text.h +++ b/src/text.h @@ -7,31 +7,7 @@ #include "common.h" #include "bitmap.h" #include "posprintf.h" - - -typedef enum { - TXT_MODE_TILED_BG, - TXT_MODE_HYBRID, - TXT_MODE_MODE3, -} TextMode; - -typedef struct Font { - // A pointer to an area of memory containing font data. - // TODO: Should we unpack each char everytime or unpack everything into RAM? - // Maybe this should be optional? - u16 *data; - // The char_map stores the index to the character position within the font - // array depending on the ascii number we want to render. This allows - // the usage reduced font sets, for example just uppercase letters and - // numbers. - u8 *char_map; - // Width and height of each font character. Only monospaced fonts are - // currently supported. - u8 char_width; - u8 char_height; - // The color of this font. - Color color; -} Font; +#include "ppu.h" typedef struct TextEngine { // Currently working on tiled backgrounds only. The X and Y positions @@ -40,85 +16,15 @@ typedef struct TextEngine { size_t cursor_x; size_t cursor_y; // Pointer to the memory being used for writing to the screen. - u16 *memory; - // TODO: Support other modes and monospaced fonts should be simple but we - // need to keep track of a couple of other variables, which may not be - // available for all modes. For example, tile modes can't have a font width - // smaller than the tile size, although bigger fonts (For example fonts - // composed of multiple tiles) should still be possible. This may not be - // a good time investment, but can be done in the future if needed. - // The mode controls how the text is rendered and how the memory is managed. - TextMode mode; + u32 *memory; // The font used to render the text. - Font font; + u8 color; } TextEngine; static TextEngine text_engine = {0}; -static u8 default_char_map[256] = {0}; - -void -txt_putc_tile(char c) { - if (c == '\0') { - return; - } - if (c == '\n') { - text_engine.cursor_x = 0; - text_engine.cursor_y++; - } else { - text_engine.memory[text_engine.cursor_x + 32 * text_engine.cursor_y] = c; - text_engine.cursor_x++; - if (text_engine.cursor_x >= 30) { - text_engine.cursor_x = 0; - text_engine.cursor_y++; - } - } - if (text_engine.cursor_y >= 20) { - text_engine.cursor_y = 0; - } -} - void -txt_putc_m3(char c) { - if (c == '\0') { - return; - } - if (c == '\n') { - text_engine.cursor_x = 0; - text_engine.cursor_y += text_engine.font.char_height; - } else { - u8 idx = text_engine.font.char_map[(int)c] * 2; - u32 *packed_char = text_engine.font.data; - packed_char += idx; - Tile tile = {0}; - unpack_tiles(packed_char, &tile, 1); - int x = text_engine.cursor_x; - int y = text_engine.cursor_y; - for (size_t i = 0; i < text_engine.font.char_height; ++i) { - for (size_t j = 0; j < text_engine.font.char_width; ++j) { - if ((tile.row[i] >> 4 * j) & 0x1) { - // put_pixel_m4(x + j, y + i, 1, backbuffer); - // TODO: Clean this up please. - put_pixel_m3(x + j, - y + i, - text_engine.font.color, - FRAMEBUFFER); - } - } - } - text_engine.cursor_x += text_engine.font.char_width; - if (text_engine.cursor_x >= SCREEN_WIDTH) { - text_engine.cursor_x = 0; - text_engine.cursor_y += text_engine.font.char_height; - } - } - if (text_engine.cursor_y >= SCREEN_HEIGHT) { - text_engine.cursor_y = 0; - } -} - -void -txt_putc_hybrid(char c) { +txt_putc(char c) { if (c == '\0') { return; } @@ -126,16 +32,9 @@ txt_putc_hybrid(char c) { text_engine.cursor_x = 0; text_engine.cursor_y++; } else { - u8 idx = text_engine.font.char_map[(int)c] * 2; - u32 *packed_char = text_engine.font.data; - packed_char += idx; - Tile tile = {0}; - unpack_tiles(packed_char, &tile, 1); int x = text_engine.cursor_x; int y = text_engine.cursor_y; - Tile *buf = text_engine.memory; - buf[x + y * 32] = tile; - dirty_tiles[y] |= 1 << x; + putfontchar(text_engine.memory, x, y, c, text_engine.color); text_engine.cursor_x += 1; if (text_engine.cursor_x >= 30) { text_engine.cursor_x = 0; @@ -147,21 +46,6 @@ txt_putc_hybrid(char c) { } } -void -txt_putc(char c) { - switch (text_engine.mode) { - case TXT_MODE_TILED_BG: { - txt_putc_tile(c); - } break; - case TXT_MODE_MODE3: { - txt_putc_m3(c); - } break; - case TXT_MODE_HYBRID: { - txt_putc_hybrid(c); - } break; - } -} - static inline void txt_puts(char *msg) { while (*msg) { @@ -170,89 +54,13 @@ txt_puts(char *msg) { } void -txt_init_tile(size_t bg, Font font, size_t cb_idx) { - // The screenblock for the tile map should start after the tile memory - // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is - // composed of 1024 screenblock entries of u16 (2 bytes), we need an - // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks. - size_t sb_idx = cb_idx * 8 + 4; - - // Set the background parameters for the text layer. - BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3); - - // Load font data in video memory. Each character is unpacked into a tile of - // 8 32bit values (4bpp), meaning that for the full ASCII set of 256 - // characters, we need 8192 bytes of VRAM (8 * 4 * 256). - text_engine.font = font; - unpack_tiles(font.data, &TILE_MEM[cb_idx], 256); - - // Initialize default values. - if (font.color == 0) { - font.color = COLOR_WHITE; - } - - // Load palette color. - PAL_BUFFER_BG[1] = font.color; - - // Update text_engine variables. - text_engine.memory = SCREENBLOCK_MEM[sb_idx]; -} - -void -txt_init_bitmap(TextMode mode, Font font) { - // If font_map is NULL, initialize the standard 0-255 character map. - if (font.char_map == NULL) { - for (size_t i = 0; i < 256; ++i) { - default_char_map[i] = i; - } - font.char_map = &default_char_map; - } - - // Initialize default values if set to zero. - if (font.char_width == 0) { - font.char_width = 8; - } - if (font.char_height == 0) { - font.char_height = 8; - } - if (font.color == 0) { - font.color = COLOR_WHITE; - } - +txt_init(u8 color, u32 *buf) { // Prepare text engine. - text_engine.font = font; - text_engine.mode = mode; -} - -void -txt_init_hybrid(TextMode mode, Font font, u32 *buf) { - // If font_map is NULL, initialize the standard 0-255 character map. - if (font.char_map == NULL) { - for (size_t i = 0; i < 256; ++i) { - default_char_map[i] = i; - } - font.char_map = &default_char_map; - } - - // Initialize default values if set to zero. - if (font.char_width == 0) { - font.char_width = 8; - } - if (font.char_height == 0) { - font.char_height = 8; - } - if (font.color == 0) { - font.color = COLOR_WHITE; - } - - // Prepare text engine. - text_engine.font = font; - text_engine.mode = mode; + text_engine.color = color; text_engine.memory = buf; } // Print text to the screen with formatting. - #define txt_printf(msg, ...) \ { \ char buf[256] = {0}; \ @@ -261,35 +69,15 @@ txt_init_hybrid(TextMode mode, Font font, u32 *buf) { } void -txt_clear_line_tiled(void) { +txt_clear_line(void) { for (size_t i = 0; i < 30; ++i) { - text_engine.memory[i + 32 * text_engine.cursor_y] = ' '; + int x = text_engine.cursor_x; + int y = text_engine.cursor_y; + putfontchar(text_engine.memory, x, y, ' ', text_engine.color); } text_engine.cursor_x = 0; } -void -txt_clear_line_bitmap(void) { - // DEBUG: should be on the text struct. - Tile *buf = &TILE_MEM[0]; - for (size_t i = 0; i < 30; ++i) { - buf[i + text_engine.cursor_y * 30] = (Tile){0}; - } -} - -void -txt_clear_line(void) { - switch (text_engine.mode) { - case TXT_MODE_TILED_BG: { - txt_clear_line_tiled(); - } break; - case TXT_MODE_MODE3: - case TXT_MODE_HYBRID: { - txt_clear_line_bitmap(); - } break; - } -} - void txt_clear_screen(void) { for (size_t j = 0; j < 20; ++j) { @@ -307,4 +95,3 @@ txt_position(size_t tile_x, size_t tile_y) { } #endif // GBAEXP_TILES_H - diff --git a/src/uxn/devices/ppu.c b/src/uxn/devices/ppu.c index 02267f0..64f3fb4 100644 --- a/src/uxn/devices/ppu.c +++ b/src/uxn/devices/ppu.c @@ -1,4 +1,5 @@ #include "ppu.h" +#include "bd-font.c" /* Copyright (c) 2021 Devine Lu Linvega @@ -14,11 +15,12 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ -#define FG_FRONT ((u32*)(MEM_VRAM)) -#define BG_FRONT ((u32*)(MEM_VRAM + KB(20))) -#define FG_BACK ((u32*)(MEM_VRAM + KB(44))) -#define BG_BACK ((u32*)(MEM_VRAM + KB(64))) -#define TILE_MAP ((u32*)(MEM_VRAM + KB(40))) +#define FG_FRONT ((u32*)(MEM_VRAM)) +#define BG_FRONT ((u32*)(MEM_VRAM + KB(20))) +#define FG_BACK ((u32*)(MEM_VRAM + KB(44))) +#define BG_BACK ((u32*)(MEM_VRAM + KB(64))) +#define TILE_MAP ((u32*)(MEM_VRAM + KB(40))) +#define FONT_DATA ((u32*)(MEM_VRAM + KB(84))) static u32 unpack_icon_lut[256] = { 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, @@ -211,6 +213,18 @@ puticn(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { dirty_tiles[(y + 7) >> 3] |= dirtyflag; } +IWRAM_CODE +void +putfontchar(u32 *layer, u16 tile_x, u16 tile_y, u8 ch, u8 color) { + u32 pos = (tile_x + tile_y * 32) * 8; + u32 *tile_data = &layer[pos]; + u32 *font_data = &FONT_DATA[8 * ch]; + for (size_t i = 0; i < 8; ++i) { + tile_data[i] = font_data[i] * color; + } + dirty_tiles[tile_y] |= 1 << tile_x; +} + IWRAM_CODE void putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, @@ -316,7 +330,7 @@ initppu(Ppu *p, u8 hor, u8 ver, u8 pad) { PAL_BUFFER_BG[2] = COLOR_RED; PAL_BUFFER_BG[3] = COLOR_BLUE; - // Initialize memory map. + // Initialize background memory map. u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg]; u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg]; size_t k = 0; @@ -325,5 +339,8 @@ initppu(Ppu *p, u8 hor, u8 ver, u8 pad) { mem_map_bg[i] = k + 32 * 4; } + // Load font data into VRAM. + unpack_tiles(&bd_font, FONT_DATA, 256); + return 1; } -- cgit v1.2.1