feat: Implement lazy loading for dungeon rooms and refactor room graphics handling

- Introduced lazy loading for room data to optimize performance and reduce initial load times.
- Updated DungeonEditor and DungeonRoomLoader to handle room graphics rendering directly from room objects.
- Refactored methods to accept room references instead of IDs for better clarity and type safety.
- Enhanced tab management in the DungeonEditor UI for improved user experience.
This commit is contained in:
scawful
2025-10-04 15:14:17 -04:00
parent 5bb696e431
commit 28dc394a7c
19 changed files with 799 additions and 468 deletions

View File

@@ -371,7 +371,7 @@ void DungeonEditor::DrawCanvasAndPropertiesPanel() {
gui::InputHexByte("Palette", &room.palette);
if (ImGui::Button("Reload Room Graphics")) {
(void)LoadAndRenderRoomGraphics(current_room);
(void)LoadAndRenderRoomGraphics(room);
}
}
@@ -555,7 +555,7 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
SameLine();
if (Button("Load Room Graphics")) {
(void)LoadAndRenderRoomGraphics(room_id);
(void)LoadAndRenderRoomGraphics(rooms_[room_id]);
}
ImGui::SameLine();
@@ -701,7 +701,7 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
if (is_loaded_) {
// Automatically load room graphics if not already loaded
if (rooms_[room_id].blocks().empty()) {
(void)LoadAndRenderRoomGraphics(room_id);
(void)LoadAndRenderRoomGraphics(rooms_[room_id]);
}
// Load room objects if not already loaded
@@ -761,7 +761,7 @@ void DungeonEditor::UpdateObjectEditor() {
// Load room graphics if not already loaded (this populates arena buffers)
if (room.blocks().empty()) {
auto status = LoadAndRenderRoomGraphics(room_id);
auto status = LoadAndRenderRoomGraphics(room);
if (!status.ok()) {
// Log error but continue
return;
@@ -829,18 +829,15 @@ void DungeonEditor::DrawObjectEditorPanels() {
}
// Legacy method implementations that delegate to components
absl::Status DungeonEditor::LoadAndRenderRoomGraphics(int room_id) {
if (room_id < 0 || room_id >= rooms_.size()) {
return absl::InvalidArgumentError("Invalid room ID");
}
return room_loader_.LoadAndRenderRoomGraphics(room_id, rooms_[room_id]);
absl::Status DungeonEditor::LoadAndRenderRoomGraphics(zelda3::Room& room) {
return room_loader_.LoadAndRenderRoomGraphics(room);
}
absl::Status DungeonEditor::ReloadAllRoomGraphics() {
return room_loader_.ReloadAllRoomGraphics(rooms_);
}
absl::Status DungeonEditor::UpdateRoomBackgroundLayers(int room_id) {
absl::Status DungeonEditor::UpdateRoomBackgroundLayers(int /*room_id*/) {
// This method is deprecated - rendering is handled by DungeonRenderer component
return absl::OkStatus();
}

View File

@@ -118,7 +118,7 @@ class DungeonEditor : public Editor {
void DrawObjectRenderer();
// Legacy methods (delegated to components)
absl::Status LoadAndRenderRoomGraphics(int room_id);
absl::Status LoadAndRenderRoomGraphics(zelda3::Room& room);
absl::Status ReloadAllRoomGraphics();
absl::Status UpdateRoomBackgroundLayers(int room_id);

View File

@@ -4,6 +4,8 @@
#include "absl/strings/str_format.h"
#include "app/gfx/snes_palette.h"
#include "app/zelda3/dungeon/room.h"
#include "app/gui/icons.h"
#include "imgui/imgui.h"
namespace yaze::editor {
@@ -28,8 +30,8 @@ absl::Status DungeonEditorV2::Load() {
return absl::FailedPreconditionError("ROM not loaded");
}
// Load all rooms using the loader component
RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_));
// Load all rooms using the loader component - DEFERRED for lazy loading
// RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_));
RETURN_IF_ERROR(room_loader_.LoadRoomEntrances(entrances_));
// Load palette group
@@ -106,7 +108,16 @@ void DungeonEditorV2::DrawLayout() {
int room_id = active_rooms_[i];
bool open = true;
if (BeginTabItem(absl::StrFormat("Room %03X", room_id).c_str(), &open)) {
std::string tab_name_str;
const char* tab_name;
if (room_id >= 0 && static_cast<size_t>(room_id) < std::size(zelda3::kRoomNames)) {
tab_name = zelda3::kRoomNames[room_id].data();
} else {
tab_name_str = absl::StrFormat("Room %03X", room_id);
tab_name = tab_name_str.c_str();
}
if (BeginTabItem(tab_name, &open)) {
DrawRoomTab(room_id);
EndTabItem();
}
@@ -116,6 +127,12 @@ void DungeonEditorV2::DrawLayout() {
i--;
}
}
// Add tab button
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) {
OnRoomSelected(room_selector_.current_room_id());
}
EndTabBar();
}
@@ -133,11 +150,21 @@ void DungeonEditorV2::DrawRoomTab(int room_id) {
return;
}
// Lazy load room data
if (!rooms_[room_id].IsLoaded()) {
auto status = room_loader_.LoadRoom(room_id, rooms_[room_id]);
if (!status.ok()) {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Failed to load room: %s",
status.message().data());
return;
}
}
// Quick controls
ImGui::Text("Room %03X", room_id);
ImGui::SameLine();
if (ImGui::Button("Load Graphics")) {
(void)room_loader_.LoadAndRenderRoomGraphics(room_id, rooms_[room_id]);
(void)room_loader_.LoadAndRenderRoomGraphics(rooms_[room_id]);
}
ImGui::SameLine();
if (ImGui::Button("Save")) {

View File

@@ -13,6 +13,20 @@
namespace yaze::editor {
absl::Status DungeonRoomLoader::LoadRoom(int room_id, zelda3::Room& room) {
if (!rom_ || !rom_->is_loaded()) {
return absl::FailedPreconditionError("ROM not loaded");
}
if (room_id < 0 || room_id >= 0x128) {
return absl::InvalidArgumentError("Invalid room ID");
}
room = zelda3::LoadRoomFromRom(rom_, room_id);
room.LoadObjects();
return absl::OkStatus();
}
absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms) {
if (!rom_ || !rom_->is_loaded()) {
return absl::FailedPreconditionError("ROM not loaded");
@@ -26,7 +40,7 @@ absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& ro
static_cast<int>(std::thread::hardware_concurrency()));
const int rooms_per_thread = (kTotalRooms + max_concurrency - 1) / max_concurrency;
util::logf("Loading %d dungeon rooms using %d threads (%d rooms per thread)",
LOG_INFO("Dungeon", "Loading %d dungeon rooms using %d threads (%d rooms per thread)",
kTotalRooms, max_concurrency, rooms_per_thread);
// Thread-safe data structures for collecting results
@@ -164,7 +178,7 @@ void DungeonRoomLoader::LoadDungeonRoomSize() {
}
}
absl::Status DungeonRoomLoader::LoadAndRenderRoomGraphics(int room_id, zelda3::Room& room) {
absl::Status DungeonRoomLoader::LoadAndRenderRoomGraphics(zelda3::Room& room) {
if (!rom_ || !rom_->is_loaded()) {
return absl::FailedPreconditionError("ROM not loaded");
}
@@ -184,8 +198,8 @@ absl::Status DungeonRoomLoader::ReloadAllRoomGraphics(std::array<zelda3::Room, 0
}
// Reload graphics for all rooms
for (size_t i = 0; i < rooms.size(); ++i) {
auto status = LoadAndRenderRoomGraphics(static_cast<int>(i), rooms[i]);
for (auto& room : rooms) {
auto status = LoadAndRenderRoomGraphics(room);
if (!status.ok()) {
continue; // Log error but continue with other rooms
}

View File

@@ -23,6 +23,7 @@ class DungeonRoomLoader {
explicit DungeonRoomLoader(Rom* rom) : rom_(rom) {}
// Room loading
absl::Status LoadRoom(int room_id, zelda3::Room& room);
absl::Status LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms);
absl::Status LoadRoomEntrances(std::array<zelda3::RoomEntrance, 0x8C>& entrances);
@@ -31,7 +32,7 @@ class DungeonRoomLoader {
uint64_t GetTotalRoomSize() const { return total_room_size_; }
// Room graphics
absl::Status LoadAndRenderRoomGraphics(int room_id, zelda3::Room& room);
absl::Status LoadAndRenderRoomGraphics(zelda3::Room& room);
absl::Status ReloadAllRoomGraphics(std::array<zelda3::Room, 0x128>& rooms);
// Data access