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 <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <future>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "app/core/performance_monitor.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/zelda3/dungeon/room.h"
|
#include "app/zelda3/dungeon/room.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
namespace yaze::editor {
|
namespace yaze::editor {
|
||||||
|
|
||||||
@@ -13,28 +18,93 @@ absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& ro
|
|||||||
return absl::FailedPreconditionError("ROM not loaded");
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dungeon_man_pal_group = rom_->palette_group().dungeon_main;
|
constexpr int kTotalRooms = 0x100 + 40; // 296 rooms
|
||||||
|
constexpr int kMaxConcurrency = 8; // Reasonable thread limit for room loading
|
||||||
|
|
||||||
for (int i = 0; i < 0x100 + 40; i++) {
|
// Determine optimal number of threads
|
||||||
rooms[i] = zelda3::LoadRoomFromRom(rom_, i);
|
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;
|
||||||
|
|
||||||
auto room_size = zelda3::CalculateRoomSize(rom_, i);
|
util::logf("Loading %d dungeon rooms using %d threads (%d rooms per thread)",
|
||||||
room_size_pointers_.push_back(room_size.room_size_pointer);
|
kTotalRooms, max_concurrency, rooms_per_thread);
|
||||||
room_sizes_.push_back(room_size.room_size);
|
|
||||||
if (room_size.room_size_pointer != 0x0A8000) {
|
// Thread-safe data structures for collecting results
|
||||||
room_size_addresses_[i] = room_size.room_size_pointer;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rooms[i].LoadObjects();
|
// Process palette results
|
||||||
|
for (const auto& [palette_id, color] : room_palette_results) {
|
||||||
auto dungeon_palette_ptr = rom_->paletteset_ids[rooms[i].palette][0];
|
room_palette_[palette_id] = color;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadDungeonRoomSize();
|
LoadDungeonRoomSize();
|
||||||
|
|||||||
Reference in New Issue
Block a user