Implement multithreaded room loading in DungeonRoomLoader
- Refactored the LoadAllRooms method to utilize multithreading for improved performance during dungeon room loading. - Introduced thread-safe data structures and a task-based approach to process room loading in parallel, optimizing resource usage. - Added performance logging to track the number of threads and rooms processed, enhancing monitoring capabilities. - Ensured thread safety when collecting results for room sizes and palettes, maintaining data integrity.
This commit is contained in:
@@ -2,9 +2,14 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "app/core/performance_monitor.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
@@ -13,30 +18,95 @@ absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& ro
|
||||
return absl::FailedPreconditionError("ROM not loaded");
|
||||
}
|
||||
|
||||
auto dungeon_man_pal_group = rom_->palette_group().dungeon_main;
|
||||
|
||||
for (int i = 0; i < 0x100 + 40; i++) {
|
||||
rooms[i] = zelda3::LoadRoomFromRom(rom_, i);
|
||||
|
||||
auto room_size = zelda3::CalculateRoomSize(rom_, i);
|
||||
room_size_pointers_.push_back(room_size.room_size_pointer);
|
||||
room_sizes_.push_back(room_size.room_size);
|
||||
if (room_size.room_size_pointer != 0x0A8000) {
|
||||
room_size_addresses_[i] = room_size.room_size_pointer;
|
||||
}
|
||||
|
||||
rooms[i].LoadObjects();
|
||||
|
||||
auto dungeon_palette_ptr = rom_->paletteset_ids[rooms[i].palette][0];
|
||||
auto palette_id = rom_->ReadWord(0xDEC4B + dungeon_palette_ptr);
|
||||
if (palette_id.status() != absl::OkStatus()) {
|
||||
continue;
|
||||
}
|
||||
int p_id = palette_id.value() / 180;
|
||||
auto color = dungeon_man_pal_group[p_id][3];
|
||||
room_palette_[rooms[i].palette] = color.rgb();
|
||||
constexpr int kTotalRooms = 0x100 + 40; // 296 rooms
|
||||
constexpr int kMaxConcurrency = 8; // Reasonable thread limit for room loading
|
||||
|
||||
// Determine optimal number of threads
|
||||
const int max_concurrency = std::min(kMaxConcurrency,
|
||||
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)",
|
||||
kTotalRooms, max_concurrency, rooms_per_thread);
|
||||
|
||||
// Thread-safe data structures for collecting results
|
||||
std::mutex results_mutex;
|
||||
std::vector<std::pair<int, zelda3::RoomSize>> room_size_results;
|
||||
std::vector<std::pair<int, ImVec4>> room_palette_results;
|
||||
|
||||
// Process rooms in parallel batches
|
||||
std::vector<std::future<absl::Status>> futures;
|
||||
|
||||
for (int thread_id = 0; thread_id < max_concurrency; ++thread_id) {
|
||||
auto task = [this, &rooms, thread_id, rooms_per_thread, &results_mutex,
|
||||
&room_size_results, &room_palette_results, kTotalRooms]() -> absl::Status {
|
||||
const int start_room = thread_id * rooms_per_thread;
|
||||
const int end_room = std::min(start_room + rooms_per_thread, kTotalRooms);
|
||||
|
||||
auto dungeon_man_pal_group = rom_->palette_group().dungeon_main;
|
||||
|
||||
for (int i = start_room; i < end_room; ++i) {
|
||||
// Load room data (this is the expensive operation)
|
||||
rooms[i] = zelda3::LoadRoomFromRom(rom_, i);
|
||||
|
||||
// Calculate room size
|
||||
auto room_size = zelda3::CalculateRoomSize(rom_, i);
|
||||
|
||||
// Load room objects
|
||||
rooms[i].LoadObjects();
|
||||
|
||||
// Process palette
|
||||
auto dungeon_palette_ptr = rom_->paletteset_ids[rooms[i].palette][0];
|
||||
auto palette_id = rom_->ReadWord(0xDEC4B + dungeon_palette_ptr);
|
||||
if (palette_id.status() == absl::OkStatus()) {
|
||||
int p_id = palette_id.value() / 180;
|
||||
auto color = dungeon_man_pal_group[p_id][3];
|
||||
|
||||
// Thread-safe collection of results
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(results_mutex);
|
||||
room_size_results.emplace_back(i, room_size);
|
||||
room_palette_results.emplace_back(rooms[i].palette, color.rgb());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
};
|
||||
|
||||
futures.emplace_back(std::async(std::launch::async, task));
|
||||
}
|
||||
|
||||
|
||||
// Wait for all threads to complete
|
||||
for (auto& future : futures) {
|
||||
RETURN_IF_ERROR(future.get());
|
||||
}
|
||||
|
||||
// Process collected results on main thread
|
||||
{
|
||||
core::ScopedTimer postprocess_timer("DungeonRoomLoader::PostProcessResults");
|
||||
|
||||
// Sort results by room ID for consistent ordering
|
||||
std::sort(room_size_results.begin(), room_size_results.end(),
|
||||
[](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
std::sort(room_palette_results.begin(), room_palette_results.end(),
|
||||
[](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
|
||||
// Process room size results
|
||||
for (const auto& [room_id, room_size] : room_size_results) {
|
||||
room_size_pointers_.push_back(room_size.room_size_pointer);
|
||||
room_sizes_.push_back(room_size.room_size);
|
||||
if (room_size.room_size_pointer != 0x0A8000) {
|
||||
room_size_addresses_[room_id] = room_size.room_size_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Process palette results
|
||||
for (const auto& [palette_id, color] : room_palette_results) {
|
||||
room_palette_[palette_id] = color;
|
||||
}
|
||||
}
|
||||
|
||||
LoadDungeonRoomSize();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user