From af79ad629cc6dd5f1c965266dabd6b064266c920 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 4 Jul 2026 13:43:23 -0400 Subject: [PATCH 1/2] MS-DOS: some optimizations for tile handling * In both the 16 color and the VESA mode, the tileset image is loaded and split into individual tiles; the tiles are then processed into a form that is compatible with the video mode in use. For 16 color mode, a tile is processed each time it is displayed, leading to slow redrawing. For VESA mode, each tile is processed at startup, leading to long startup times. Both modes are changed so that the tile is processed once, when it is first displayed, and the result is cached. * Use memcpy when splitting the image into tiles. * Only load the tileset once. In 16 color mode, for reasons I do not understand, the gr_init function is called twice, leading to delay in startup. This does not happen in VESA mode. --- sys/msdos/vidvesa.c | 83 +++++++++++++++++++++++++++------------ sys/msdos/vidvga.c | 96 +++++++++++++++++++++++++++++++++------------ win/share/tileset.c | 24 ++++++------ 3 files changed, 143 insertions(+), 60 deletions(-) diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index bb66379ad..147e21da3 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -60,6 +60,8 @@ static unsigned long vesa_DoublePixels(unsigned long); static unsigned long vesa_TriplePixels(unsigned long); static void vesa_WriteStr(const char *, int, int, int, int); static unsigned char __far *vesa_FontPtrs(void); +static unsigned char *vesa_tile(unsigned); +static unsigned char *vesa_oview_tile(unsigned); /* static void vesa_process_tile(struct TileImage *tile); */ #ifdef POSITIONBAR @@ -849,7 +851,7 @@ vesa_redrawmap(void) for (cy = 0; cy < ROWNO; ++cy) { for (py = 0; py < vesa_oview_height; ++py) { for (cx = 0; cx < COLNO; ++cx) { - tile = vesa_oview_tiles[map[cy][cx].tileidx]; + tile = vesa_oview_tile(map[cy][cx].tileidx); vesa_WritePixelRow(offset + p_row_width * cx, tile + p_row_width * py, p_row_width); } x = COLNO * vesa_oview_width; @@ -869,7 +871,7 @@ vesa_redrawmap(void) for (cy = clipy; cy <= (unsigned) clipymax && cy < ROWNO; ++cy) { for (py = 0; py < (unsigned) iflags.wc_tile_height; ++py) { for (cx = clipx; cx <= (unsigned) clipxmax && cx < COLNO; ++cx) { - tile = vesa_tiles[map[cy][cx].tileidx]; + tile = vesa_tile(map[cy][cx].tileidx); vesa_WritePixelRow(offset + p_row_width * (cx - clipx), tile + p_row_width * py, p_row_width); } x = (cx - clipx) * iflags.wc_tile_width; @@ -1053,8 +1055,6 @@ vesa_Init(void) { static boolean inited = FALSE; const struct Pixel *paletteptr; - unsigned i; - unsigned num_pixels, num_oview_pixels; const char *tile_file; const char *font_name; int tilefailure = 0; @@ -1186,31 +1186,33 @@ vesa_Init(void) /* Process tiles for the current video mode */ vesa_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); + memset(vesa_tiles, 0, total_tiles_used * sizeof(void *)); vesa_oview_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *)); - num_pixels = iflags.wc_tile_width * iflags.wc_tile_height; - num_oview_pixels = vesa_oview_width * vesa_oview_height; + memset(vesa_oview_tiles, 0, total_tiles_used * sizeof(void *)); set_tile_type(vesa_pixel_size > 8); - for (i = 0; i < (unsigned) total_tiles_used; ++i) { - const struct TileImage *tile = get_tile(i); - struct TileImage *ov_tile = stretch_tile(tile, vesa_oview_width, vesa_oview_height); +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* Return processed pixels for a normal tile */ +static unsigned char * +vesa_tile(unsigned index) +{ + if (vesa_tiles[index] == NULL) { + unsigned num_pixels = iflags.wc_tile_width * iflags.wc_tile_height; + const struct TileImage *tile = get_tile(index); unsigned j; unsigned char *t_img = (unsigned char *) alloc(num_pixels * vesa_pixel_bytes); - unsigned char *ot_img = (unsigned char *) alloc(num_oview_pixels * vesa_pixel_bytes); - vesa_tiles[i] = t_img; - vesa_oview_tiles[i] = ot_img; + vesa_tiles[index] = t_img; switch (vesa_pixel_bytes) { case 1: memcpy(t_img, tile->indexes, num_pixels); - memcpy(ot_img, ov_tile->indexes, num_oview_pixels); break; case 2: for (j = 0; j < num_pixels; ++j) { ((uint16_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); } - for (j = 0; j < num_oview_pixels; ++j) { - ((uint16_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); - } break; case 3: @@ -1220,6 +1222,42 @@ vesa_Init(void) t_img[3*j + 1] = (color >> 8) & 0xFF; t_img[3*j + 2] = (color >> 16) & 0xFF; } + break; + + case 4: + for (j = 0; j < num_pixels; ++j) { + ((uint32_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); + } + break; + } + } + + return vesa_tiles[index]; +} + +/* Return processed pixels for an overview tile */ +static unsigned char * +vesa_oview_tile(unsigned index) +{ + if (vesa_oview_tiles[index] == NULL) { + unsigned num_oview_pixels = vesa_oview_width * vesa_oview_height; + const struct TileImage *tile = get_tile(index); + struct TileImage *ov_tile = stretch_tile(tile, vesa_oview_width, vesa_oview_height); + unsigned j; + unsigned char *ot_img = (unsigned char *) alloc(num_oview_pixels * vesa_pixel_bytes); + vesa_oview_tiles[index] = ot_img; + switch (vesa_pixel_bytes) { + case 1: + memcpy(ot_img, ov_tile->indexes, num_oview_pixels); + break; + + case 2: + for (j = 0; j < num_oview_pixels; ++j) { + ((uint16_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); + } + break; + + case 3: for (j = 0; j < num_oview_pixels; ++j) { unsigned long color = vesa_MakeColor(ov_tile->pixels[j]); ot_img[3*j + 0] = color & 0xFF; @@ -1229,9 +1267,6 @@ vesa_Init(void) break; case 4: - for (j = 0; j < num_pixels; ++j) { - ((uint32_t *)t_img)[j] = vesa_MakeColor(tile->pixels[j]); - } for (j = 0; j < num_oview_pixels; ++j) { ((uint32_t *)ot_img)[j] = vesa_MakeColor(ov_tile->pixels[j]); } @@ -1239,10 +1274,9 @@ vesa_Init(void) } free_tile(ov_tile); } - free_tiles(); -} -RESTORE_WARNING_FORMAT_NONLITERAL + return vesa_oview_tiles[index]; +} /* Set the size of the map viewport */ static void @@ -1314,6 +1348,7 @@ vesa_Finish(void) } free(vesa_tiles); free(vesa_oview_tiles); + free_tiles(); vesa_SwitchMode(MODETEXT); windowprocs.win_cliparound = tty_cliparound; g_attribute = attrib_text_normal; @@ -1873,11 +1908,11 @@ vesa_DisplayCell(int tilenum, int col, int row) unsigned p_row_width; if (iflags.over_view) { - tile = vesa_oview_tiles[tilenum]; + tile = vesa_oview_tile(tilenum); t_width = vesa_oview_width; t_height = vesa_oview_height; } else { - tile = vesa_tiles[tilenum]; + tile = vesa_tile(tilenum); t_width = iflags.wc_tile_width; t_height = iflags.wc_tile_height; } diff --git a/sys/msdos/vidvga.c b/sys/msdos/vidvga.c index 792a32e2b..a546d67c1 100644 --- a/sys/msdos/vidvga.c +++ b/sys/msdos/vidvga.c @@ -213,8 +213,8 @@ int vp[SCREENPLANES] = { 8, 4, 2, 1 }; #endif int vp2[SCREENPLANES] = { 1, 2, 4, 8 }; -static struct planar_cell_struct planecell; -static struct overview_planar_cell_struct planecell_O; +static struct planar_cell_struct **cell_cache; +static struct overview_planar_cell_struct **cell_cache_O; /* static int g_attribute; */ /* Current attribute to use */ @@ -410,6 +410,7 @@ vga_xputg(const glyph_info *glyphinfo, vga_WriteChar(ch, col, row, attr); } else if (!iflags.over_view) { if ((col >= clipx) && (col <= clipxmax)) { + struct planar_cell_struct planecell; read_planar_tile(glyphnum, &planecell); if (map[ry][col].special) decal_planar(&planecell, special); @@ -426,6 +427,7 @@ vga_xputg(const glyph_info *glyphinfo, } } } else { + struct overview_planar_cell_struct planecell_O; read_planar_tile_O(glyphnum, &planecell_O); vga_DisplayCell_O(&planecell_O, col, row); } @@ -512,11 +514,13 @@ vga_redrawmap(boolean clearfirst) } else { t = map[y][x].glyph; if (!iflags.over_view) { + struct planar_cell_struct planecell; read_planar_tile(t, &planecell); if (map[y][x].special) decal_planar(&planecell, map[y][x].special); vga_DisplayCell(&planecell, x - clipx, y + TOP_MAP_ROW); } else { + struct overview_planar_cell_struct planecell_O; read_planar_tile_O(t, &planecell_O); vga_DisplayCell_O(&planecell_O, x, y + TOP_MAP_ROW); } @@ -643,6 +647,7 @@ boolean left; } for (y = 0; y < ROWNO; ++y) { for (x = i; x < j; x += 2) { + struct planar_cell_struct planecell; t = map[y][x].glyph; read_planar_tile(t, &planecell); if (map[y][x].special) @@ -656,47 +661,69 @@ boolean left; static void read_planar_tile(unsigned glyph, struct planar_cell_struct *cell) { + struct planar_cell_struct *pcell; unsigned char indexes[TILE_Y][TILE_X]; unsigned plane, y, byte, bit; + int tilenum = glyphmap[glyph].tileidx; - read_tile_indexes(glyph, indexes); - /* cell->plane[0..3].image[0..15][0..1] */ - for (plane = 0; plane < SCREENPLANES; ++plane) { - for (y = 0; y < TILE_Y; ++y) { - for (byte = 0; byte < MAX_BYTES_PER_CELL; ++byte) { - unsigned char b = 0; - for (bit = 0; bit < 8; ++bit) { - unsigned char x = byte * 8 + bit; - unsigned char i = indexes[y][x]; - b <<= 1; - if (i & (0x8 >> plane)) b |= 1; + /* Get the processed tile from the cache if we can */ + pcell = cell_cache[tilenum]; + if (pcell == NULL) { + /* Process the tile */ + pcell = (struct planar_cell_struct *) alloc(sizeof(*pcell)); + cell_cache[tilenum] = pcell; + read_tile_indexes(glyph, indexes); + /* pcell->plane[0..3].image[0..15][0..1] */ + for (plane = 0; plane < SCREENPLANES; ++plane) { + for (y = 0; y < TILE_Y; ++y) { + for (byte = 0; byte < MAX_BYTES_PER_CELL; ++byte) { + unsigned char b = 0; + for (bit = 0; bit < 8; ++bit) { + unsigned char x = byte * 8 + bit; + unsigned char i = indexes[y][x]; + b <<= 1; + if (i & (0x8 >> plane)) b |= 1; + } + pcell->plane[plane].image[y][byte] = b; } - cell->plane[plane].image[y][byte] = b; } } } + + *cell = *pcell; } static void read_planar_tile_O(unsigned glyph, struct overview_planar_cell_struct *cell) { + struct overview_planar_cell_struct *pcell; unsigned char indexes[TILE_Y][TILE_X]; unsigned plane, y, bit; + int tilenum = glyphmap[glyph].tileidx; - read_tile_indexes(glyph, indexes); - /* cell->plane[0..3].image[0..15][0..0] */ - for (plane = 0; plane < SCREENPLANES; ++plane) { - for (y = 0; y < TILE_Y; ++y) { - unsigned char b = 0; - for (bit = 0; bit < 8; ++bit) { - unsigned char x = bit * 2; - unsigned char i = indexes[y][x]; - b <<= 1; - if (i & (0x8 >> plane)) b |= 1; + /* Get the processed tile from the cache if we can */ + pcell = cell_cache_O[tilenum]; + if (pcell == NULL) { + /* Process the tile */ + pcell = (struct overview_planar_cell_struct *) alloc(sizeof(*pcell)); + cell_cache_O[tilenum] = pcell; + read_tile_indexes(glyph, indexes); + /* pcell->plane[0..3].image[0..15][0..0] */ + for (plane = 0; plane < SCREENPLANES; ++plane) { + for (y = 0; y < TILE_Y; ++y) { + unsigned char b = 0; + for (bit = 0; bit < 8; ++bit) { + unsigned char x = bit * 2; + unsigned char i = indexes[y][x]; + b <<= 1; + if (i & (0x8 >> plane)) b |= 1; + } + pcell->plane[plane].image[y][0] = b; } - cell->plane[plane].image[y][0] = b; } } + + *cell = *pcell; } static void @@ -782,6 +809,15 @@ vga_Init(void) /* term_clear_screen() */ /* not vga_clear_screen() */ return; } + + if (cell_cache == NULL) { + cell_cache = (struct planar_cell_struct **) alloc( + total_tiles_used * sizeof(cell_cache[0])); + memset(cell_cache, 0, total_tiles_used * sizeof(cell_cache[0])); + cell_cache_O = (struct overview_planar_cell_struct **) alloc( + total_tiles_used * sizeof(cell_cache_O[0])); + memset(cell_cache_O, 0, total_tiles_used * sizeof(cell_cache_O[0])); + } #endif if (iflags.usevga) { @@ -860,7 +896,17 @@ vga_SwitchMode(unsigned int mode) void vga_Finish(void) { + int i; + free_tiles(); + for (i = 0; i < total_tiles_used; ++i) { + free(cell_cache[i]); + free(cell_cache_O[i]); + } + free(cell_cache); + cell_cache = NULL; + free(cell_cache_O); + cell_cache_O = NULL; vga_SwitchMode(MODETEXT); windowprocs.win_cliparound = tty_cliparound; g_attribute = attrib_text_normal; diff --git a/win/share/tileset.c b/win/share/tileset.c index 2fff3affa..385d26580 100644 --- a/win/share/tileset.c +++ b/win/share/tileset.c @@ -30,6 +30,9 @@ read_tiles(const char *filename, boolean true_color) char header[16]; boolean ok; + if (tiles != NULL) + return; + /* Fill the image structure with known values */ image.width = 0; image.height = 0; @@ -283,7 +286,7 @@ split_tiles(const struct TileSetImage *image) { unsigned tile_rows, tile_cols; size_t tile_size, i, j; - unsigned x1, y1, x2, y2; + unsigned x1, y1, y2; /* Get the number of tiles */ tile_rows = image->height / iflags.wc_tile_height; @@ -308,16 +311,15 @@ split_tiles(const struct TileSetImage *image) tile->indexes = (unsigned char *) alloc(tile_size); } for (y2 = 0; y2 < (unsigned) iflags.wc_tile_height; ++y2) { - for (x2 = 0; x2 < (unsigned) iflags.wc_tile_width; ++x2) { - unsigned x = x1 * iflags.wc_tile_width + x2; - unsigned y = y1 * iflags.wc_tile_height + y2; - - i = y * image->width + x; - j = y2 * tile->width + x2; - tile->pixels[j] = image->pixels[i]; - if (image->indexes != NULL) { - tile->indexes[j] = image->indexes[i]; - } + unsigned y = y1 * iflags.wc_tile_height + y2; + unsigned x = x1 * iflags.wc_tile_width; + i = y * image->width; + j = y2 * tile->width; + memcpy(tile->pixels + j, image->pixels + i + x, + sizeof(tile->pixels[0]) * iflags.wc_tile_width); + if (image->indexes != NULL) { + memcpy(tile->indexes + j, image->indexes + i + x, + iflags.wc_tile_width); } } } From 12560f9b137e2da1ffff631ee1cc705a62b810d3 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sun, 5 Jul 2026 22:26:42 -0400 Subject: [PATCH 2/2] Add return value where needed. --- win/share/tileset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/share/tileset.c b/win/share/tileset.c index 385d26580..c3fbb209a 100644 --- a/win/share/tileset.c +++ b/win/share/tileset.c @@ -31,7 +31,7 @@ read_tiles(const char *filename, boolean true_color) boolean ok; if (tiles != NULL) - return; + return TRUE; /* Fill the image structure with known values */ image.width = 0;