fix: Improve tile drawing logic and optimize texture processing

- Enhanced the DrawTileToBitmap function to correctly calculate tile positions within the graphics buffer, addressing potential out-of-bounds issues.
- Added detailed comments to clarify the organization of tile sheets and pixel indexing.
- Optimized the RenderRoomGraphics function by deferring texture processing to batch updates, significantly improving performance when multiple rooms are open.
This commit is contained in:
scawful
2025-10-10 13:55:39 -04:00
parent fefd60da6e
commit 8c26f17594
2 changed files with 52 additions and 22 deletions

View File

@@ -830,9 +830,25 @@ void ObjectDrawer::DrawTileToBitmap(gfx::Bitmap& bitmap, const gfx::TileInfo& ti
return; return;
} }
// Calculate tile position in graphics sheet (128 pixels wide, 16 tiles per row) // CRITICAL FIX: current_gfx16_ is organized as 16 sheets of 128x128 pixels
int tile_sheet_x = (tile_info.id_ % 16) * 8; // 16 tiles per row // Each sheet is 0x800 bytes (2048 bytes = 128*128/8 = 16384 pixels)
int tile_sheet_y = (tile_info.id_ / 16) * 8; // Each row is 16 tiles // Total buffer size: 16 * 0x800 = 0x8000 bytes (32768 bytes)
// Tile IDs are 0-511, distributed across sheets:
// Sheet 0: tiles 0-127
// Sheet 1: tiles 128-255
// ...
// Sheet 15: tiles 480-511 (but only uses first 32 tiles)
//
// Within each sheet, tiles are arranged in a 16x8 grid (128 tiles total):
// Row 0: tiles 0-15
// Row 1: tiles 16-31
// ...
// Row 7: tiles 112-127
int sheet_index = tile_info.id_ / 128; // Which 128-tile sheet (0-15)?
int tile_in_sheet = tile_info.id_ % 128; // Which tile within that sheet (0-127)?
int tile_x_in_sheet = (tile_in_sheet % 16) * 8; // 16 tiles per row, 8 pixels each
int tile_y_in_sheet = (tile_in_sheet / 16) * 8; // 8 rows, 8 pixels each
// Palettes are 3bpp (8 colors). Convert palette index to base color offset. // Palettes are 3bpp (8 colors). Convert palette index to base color offset.
uint8_t palette_offset = (tile_info.palette_ & 0x0F) * 8; uint8_t palette_offset = (tile_info.palette_ & 0x0F) * 8;
@@ -844,12 +860,23 @@ void ObjectDrawer::DrawTileToBitmap(gfx::Bitmap& bitmap, const gfx::TileInfo& ti
int src_x = tile_info.horizontal_mirror_ ? (7 - px) : px; int src_x = tile_info.horizontal_mirror_ ? (7 - px) : px;
int src_y = tile_info.vertical_mirror_ ? (7 - py) : py; int src_y = tile_info.vertical_mirror_ ? (7 - py) : py;
// Read pixel from graphics sheet // Calculate source pixel index in current_gfx16_ buffer
int src_index = (tile_sheet_y + src_y) * 128 + (tile_sheet_x + src_x); // Buffer layout: [Sheet 0: 0x800 bytes][Sheet 1: 0x800 bytes]...[Sheet 15: 0x800 bytes]
// Within each sheet: row-major order, 128 pixels per row
int src_index = (sheet_index * 0x800) + // Sheet offset (2048 bytes per sheet)
(tile_y_in_sheet + src_y) * 128 + // Row within sheet (128 pixels/row)
(tile_x_in_sheet + src_x); // Column within row
// Bounds check for graphics buffer
if (src_index < 0 || src_index >= 0x4000) {
continue; // Out of bounds, skip pixel
}
uint8_t pixel_index = tiledata[src_index]; uint8_t pixel_index = tiledata[src_index];
if (pixel_index == 0) { if (pixel_index == 0) {
continue; continue; // Transparent pixel
} }
uint8_t final_color = pixel_index + palette_offset; uint8_t final_color = pixel_index + palette_offset;
int dest_x = pixel_x + px; int dest_x = pixel_x + px;
int dest_y = pixel_y + py; int dest_y = pixel_y + py;

View File

@@ -335,27 +335,30 @@ void Room::RenderRoomGraphics() {
// ObjectDrawer will write indexed pixel data that uses the palette we just set // ObjectDrawer will write indexed pixel data that uses the palette we just set
RenderObjectsToBackground(); RenderObjectsToBackground();
// Update textures with all the data (floor + background + objects + palette) // PERFORMANCE OPTIMIZATION: Queue texture commands but DON'T process immediately
// This allows multiple rooms to batch their texture updates together
// The dungeon_canvas_viewer.cc:552 will process all queued textures once per frame
if (bg1_bmp.texture()) { if (bg1_bmp.texture()) {
// Texture exists - UPDATE it with new object data // Texture exists - UPDATE it with new object data
LOG_DEBUG("[RenderRoomGraphics]", "Queueing UPDATE for existing textures"); LOG_DEBUG("[RenderRoomGraphics]", "Queueing UPDATE for existing textures (deferred)");
gfx::Arena::Get().QueueTextureCommand( gfx::Arena::Get().QueueTextureCommand(
gfx::Arena::TextureCommandType::UPDATE, &bg1_bmp); gfx::Arena::TextureCommandType::UPDATE, &bg1_bmp);
gfx::Arena::Get().QueueTextureCommand( gfx::Arena::Get().QueueTextureCommand(
gfx::Arena::TextureCommandType::UPDATE, &bg2_bmp); gfx::Arena::TextureCommandType::UPDATE, &bg2_bmp);
} else { } else {
// No texture yet - CREATE it // No texture yet - CREATE it
LOG_DEBUG("[RenderRoomGraphics]", "Queueing CREATE for new textures"); LOG_DEBUG("[RenderRoomGraphics]", "Queueing CREATE for new textures (deferred)");
gfx::Arena::Get().QueueTextureCommand( gfx::Arena::Get().QueueTextureCommand(
gfx::Arena::TextureCommandType::CREATE, &bg1_bmp); gfx::Arena::TextureCommandType::CREATE, &bg1_bmp);
gfx::Arena::Get().QueueTextureCommand( gfx::Arena::Get().QueueTextureCommand(
gfx::Arena::TextureCommandType::CREATE, &bg2_bmp); gfx::Arena::TextureCommandType::CREATE, &bg2_bmp);
} }
// CRITICAL: Process texture queue immediately so objects appear! // REMOVED: Don't process texture queue here - let it be batched!
// Don't wait for next frame - update NOW! // Processing happens once per frame in DrawDungeonCanvas()
gfx::Arena::Get().ProcessTextureQueue(nullptr); // This dramatically improves performance when multiple rooms are open
LOG_DEBUG("[RenderRoomGraphics]", "Processed texture queue immediately"); // gfx::Arena::Get().ProcessTextureQueue(nullptr); // OLD: Caused slowdown!
LOG_DEBUG("[RenderRoomGraphics]", "Texture commands queued for batch processing");
} }
void Room::LoadLayoutTilesToBuffer() { void Room::LoadLayoutTilesToBuffer() {