mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Cache MPQ file->archive lookups to prevent character select stalls
This commit is contained in:
parent
652f9e64fc
commit
f5f757332a
2 changed files with 72 additions and 6 deletions
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
|
||||
|
|
@ -108,6 +109,12 @@ private:
|
|||
|
||||
void logMissingFileOnce(const std::string& filename) const;
|
||||
|
||||
// Cache for mapping "virtual filename" -> archive handle (or INVALID_HANDLE_VALUE for not found).
|
||||
// This avoids scanning every archive for repeated lookups, which can otherwise appear as a hang
|
||||
// on screens that trigger many asset probes (character select, character preview, etc.).
|
||||
mutable std::mutex fileArchiveCacheMutex_;
|
||||
mutable std::unordered_map<std::string, HANDLE> fileArchiveCache_;
|
||||
|
||||
mutable std::mutex missingFileMutex_;
|
||||
mutable std::unordered_set<std::string> missingFileWarnings_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "pipeline/mpq_manager.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
|
@ -28,6 +29,16 @@ std::string toLowerCopy(std::string value) {
|
|||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string normalizeVirtualFilenameForLookup(std::string value) {
|
||||
// StormLib uses backslash-separated virtual paths; treat lookups as case-insensitive.
|
||||
std::replace(value.begin(), value.end(), '/', '\\');
|
||||
value = toLowerCopy(std::move(value));
|
||||
while (!value.empty() && (value.front() == '\\' || value.front() == '/')) {
|
||||
value.erase(value.begin());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
MPQManager::MPQManager() = default;
|
||||
|
|
@ -104,6 +115,10 @@ void MPQManager::shutdown() {
|
|||
|
||||
archives.clear();
|
||||
archiveNames.clear();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fileArchiveCacheMutex_);
|
||||
fileArchiveCache_.clear();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(missingFileMutex_);
|
||||
missingFileWarnings_.clear();
|
||||
|
|
@ -144,6 +159,12 @@ bool MPQManager::loadArchive(const std::string& path, int priority) {
|
|||
return a.priority > b.priority;
|
||||
});
|
||||
|
||||
// Archive set/priority changed, so cached filename -> archive mappings may be stale.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fileArchiveCacheMutex_);
|
||||
fileArchiveCache_.clear();
|
||||
}
|
||||
|
||||
LOG_INFO("Loaded MPQ archive: ", path, " (priority ", priority, ")");
|
||||
return true;
|
||||
#endif
|
||||
|
|
@ -175,9 +196,11 @@ std::vector<uint8_t> MPQManager::readFile(const std::string& filename) const {
|
|||
if (!archives.empty()) {
|
||||
HANDLE archive = findFileArchive(filename);
|
||||
if (archive != INVALID_HANDLE_VALUE) {
|
||||
std::string stormFilename = filename;
|
||||
std::replace(stormFilename.begin(), stormFilename.end(), '/', '\\');
|
||||
// Open the file
|
||||
HANDLE file = INVALID_HANDLE_VALUE;
|
||||
if (SFileOpenFileEx(archive, filename.c_str(), 0, &file)) {
|
||||
if (SFileOpenFileEx(archive, stormFilename.c_str(), 0, &file)) {
|
||||
// Get file size
|
||||
DWORD fileSize = SFileGetFileSize(file, nullptr);
|
||||
if (fileSize > 0 && fileSize != SFILE_INVALID_SIZE) {
|
||||
|
|
@ -283,8 +306,10 @@ uint32_t MPQManager::getFileSize(const std::string& filename) const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::string stormFilename = filename;
|
||||
std::replace(stormFilename.begin(), stormFilename.end(), '/', '\\');
|
||||
HANDLE file = INVALID_HANDLE_VALUE;
|
||||
if (!SFileOpenFileEx(archive, filename.c_str(), 0, &file)) {
|
||||
if (!SFileOpenFileEx(archive, stormFilename.c_str(), 0, &file)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -303,12 +328,46 @@ HANDLE MPQManager::findFileArchive(const std::string& filename) const {
|
|||
#endif
|
||||
|
||||
#ifdef HAVE_STORMLIB
|
||||
// Search archives in priority order (already sorted)
|
||||
for (const auto& entry : archives) {
|
||||
if (SFileHasFile(entry.handle, filename.c_str())) {
|
||||
return entry.handle;
|
||||
std::string cacheKey = normalizeVirtualFilenameForLookup(filename);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fileArchiveCacheMutex_);
|
||||
auto it = fileArchiveCache_.find(cacheKey);
|
||||
if (it != fileArchiveCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string stormFilename = filename;
|
||||
std::replace(stormFilename.begin(), stormFilename.end(), '/', '\\');
|
||||
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
HANDLE found = INVALID_HANDLE_VALUE;
|
||||
// Search archives in priority order (already sorted)
|
||||
for (const auto& entry : archives) {
|
||||
if (SFileHasFile(entry.handle, stormFilename.c_str())) {
|
||||
found = entry.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fileArchiveCacheMutex_);
|
||||
// Another thread may have raced to populate; if so, prefer the existing value.
|
||||
auto [it, inserted] = fileArchiveCache_.emplace(std::move(cacheKey), found);
|
||||
if (!inserted) {
|
||||
found = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// With caching this should only happen once per unique filename; keep threshold conservative.
|
||||
if (ms >= 100) {
|
||||
LOG_WARNING("Slow MPQ lookup: '", filename, "' scanned ", archives.size(), " archives in ", ms, " ms");
|
||||
}
|
||||
|
||||
return found;
|
||||
#endif
|
||||
|
||||
return INVALID_HANDLE_VALUE;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue