mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 00:20:16 +00:00
Fix WAV decode cache and spell lookup performance
- audio_engine.cpp: cache key was based on vector pointer address, not content — cache never hit since each asset load produces a new allocation. Replace with FNV-1a over head+tail bytes + size, giving correct content-based identity at O(1) cost. Add 256-entry cap with eviction to prevent unbounded growth over long sessions. - game_handler.hpp/cpp, spellbook_screen, game_screen: knownSpells was a std::vector with O(n) std::find lookups scattered across packet handlers and UI code. Switch to std::unordered_set for O(1) insert, erase, and membership checks. Update all push_back/erase-remove/find call sites to use set operations.
This commit is contained in:
parent
e29b67dad9
commit
827679579f
6 changed files with 47 additions and 36 deletions
|
|
@ -417,7 +417,7 @@ public:
|
|||
void castSpell(uint32_t spellId, uint64_t targetGuid = 0);
|
||||
void cancelCast();
|
||||
void cancelAura(uint32_t spellId);
|
||||
const std::vector<uint32_t>& getKnownSpells() const { return knownSpells; }
|
||||
const std::unordered_set<uint32_t>& getKnownSpells() const { return knownSpells; }
|
||||
bool isCasting() const { return casting; }
|
||||
uint32_t getCurrentCastSpellId() const { return currentCastSpellId; }
|
||||
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
||||
|
|
@ -1315,7 +1315,7 @@ private:
|
|||
uint64_t playerTransportStickyGuid_ = 0; // Last transport player was on (temporary retention)
|
||||
float playerTransportStickyTimer_ = 0.0f; // Seconds to keep sticky transport alive after transient clears
|
||||
std::unique_ptr<TransportManager> transportManager_; // Transport movement manager
|
||||
std::vector<uint32_t> knownSpells;
|
||||
std::unordered_set<uint32_t> knownSpells;
|
||||
std::unordered_map<uint32_t, float> spellCooldowns; // spellId -> remaining seconds
|
||||
uint8_t castCount = 0;
|
||||
bool casting = false;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ private:
|
|||
void loadSpellDBC(pipeline::AssetManager* assetManager);
|
||||
void loadSpellIconDBC(pipeline::AssetManager* assetManager);
|
||||
void loadSkillLineDBCs(pipeline::AssetManager* assetManager);
|
||||
void categorizeSpells(const std::vector<uint32_t>& knownSpells);
|
||||
void categorizeSpells(const std::unordered_set<uint32_t>& knownSpells);
|
||||
GLuint getSpellIcon(uint32_t iconId, pipeline::AssetManager* assetManager);
|
||||
const SpellInfo* getSpellInfo(uint32_t spellId) const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,9 +25,25 @@ struct DecodedWavCacheEntry {
|
|||
static std::unordered_map<uint64_t, DecodedWavCacheEntry> gDecodedWavCache;
|
||||
|
||||
static uint64_t makeWavCacheKey(const std::vector<uint8_t>& wavData) {
|
||||
uint64_t ptr = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(wavData.data()));
|
||||
uint64_t sz = static_cast<uint64_t>(wavData.size());
|
||||
return (ptr * 11400714819323198485ull) ^ (sz + 0x9e3779b97f4a7c15ull + (ptr << 6) + (ptr >> 2));
|
||||
// FNV-1a over the first 256 bytes + last 256 bytes + total size.
|
||||
// Full-content hash would be correct but slow for large files; sampling the
|
||||
// edges catches virtually all distinct files while keeping cost O(1).
|
||||
constexpr uint64_t FNV_OFFSET = 14695981039346656037ull;
|
||||
constexpr uint64_t FNV_PRIME = 1099511628211ull;
|
||||
uint64_t h = FNV_OFFSET;
|
||||
auto mix = [&](uint8_t b) { h ^= b; h *= FNV_PRIME; };
|
||||
|
||||
const size_t sz = wavData.size();
|
||||
const size_t head = std::min(sz, size_t(256));
|
||||
for (size_t i = 0; i < head; ++i) mix(wavData[i]);
|
||||
if (sz > 256) {
|
||||
const size_t tail_start = sz > 512 ? sz - 256 : 256;
|
||||
for (size_t i = tail_start; i < sz; ++i) mix(wavData[i]);
|
||||
}
|
||||
// Mix in the total size so files with identical head/tail but different
|
||||
// lengths still produce different keys.
|
||||
for (int s = 0; s < 8; ++s) mix(static_cast<uint8_t>(sz >> (s * 8)));
|
||||
return h;
|
||||
}
|
||||
|
||||
static bool decodeWavCached(const std::vector<uint8_t>& wavData, DecodedWavCacheEntry& out) {
|
||||
|
|
@ -80,6 +96,12 @@ static bool decodeWavCached(const std::vector<uint8_t>& wavData, DecodedWavCache
|
|||
entry.sampleRate = sampleRate;
|
||||
entry.frames = framesRead;
|
||||
entry.pcmData = pcmData;
|
||||
// Evict oldest half when cache grows too large (keeps ~128 most-recent sounds)
|
||||
if (gDecodedWavCache.size() >= 256) {
|
||||
auto it = gDecodedWavCache.begin();
|
||||
for (size_t n = gDecodedWavCache.size() / 2; n > 0; --n, ++it) {}
|
||||
gDecodedWavCache.erase(gDecodedWavCache.begin(), it);
|
||||
}
|
||||
gDecodedWavCache.emplace(key, entry);
|
||||
out = entry;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -96,8 +96,8 @@ GameHandler::GameHandler() {
|
|||
wardenModuleManager_ = std::make_unique<WardenModuleManager>();
|
||||
|
||||
// Default spells always available
|
||||
knownSpells.push_back(6603); // Attack
|
||||
knownSpells.push_back(8690); // Hearthstone
|
||||
knownSpells.insert(6603); // Attack
|
||||
knownSpells.insert(8690); // Hearthstone
|
||||
|
||||
// Default action bar layout
|
||||
actionBar[0].type = ActionBarSlot::SPELL;
|
||||
|
|
@ -1053,9 +1053,8 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
|
||||
// Add to known spells immediately for prerequisite re-evaluation
|
||||
// (SMSG_LEARNED_SPELL may come separately, but we need immediate update)
|
||||
bool alreadyKnown = std::find(knownSpells.begin(), knownSpells.end(), spellId) != knownSpells.end();
|
||||
if (!alreadyKnown) {
|
||||
knownSpells.push_back(spellId);
|
||||
if (!knownSpells.count(spellId)) {
|
||||
knownSpells.insert(spellId);
|
||||
LOG_INFO("Added spell ", spellId, " to known spells (trainer purchase)");
|
||||
}
|
||||
|
||||
|
|
@ -7010,21 +7009,17 @@ void GameHandler::handleInitialSpells(network::Packet& packet) {
|
|||
InitialSpellsData data;
|
||||
if (!InitialSpellsParser::parse(packet, data)) return;
|
||||
|
||||
knownSpells = data.spellIds;
|
||||
knownSpells = {data.spellIds.begin(), data.spellIds.end()};
|
||||
|
||||
// Debug: check if specific spells are in initial spells
|
||||
bool has527 = std::find(knownSpells.begin(), knownSpells.end(), 527u) != knownSpells.end();
|
||||
bool has988 = std::find(knownSpells.begin(), knownSpells.end(), 988u) != knownSpells.end();
|
||||
bool has1180 = std::find(knownSpells.begin(), knownSpells.end(), 1180u) != knownSpells.end();
|
||||
bool has527 = knownSpells.count(527u);
|
||||
bool has988 = knownSpells.count(988u);
|
||||
bool has1180 = knownSpells.count(1180u);
|
||||
LOG_INFO("Initial spells include: 527=", has527, " 988=", has988, " 1180=", has1180);
|
||||
|
||||
// Ensure Attack (6603) and Hearthstone (8690) are always present
|
||||
if (std::find(knownSpells.begin(), knownSpells.end(), 6603u) == knownSpells.end()) {
|
||||
knownSpells.insert(knownSpells.begin(), 6603u);
|
||||
}
|
||||
if (std::find(knownSpells.begin(), knownSpells.end(), 8690u) == knownSpells.end()) {
|
||||
knownSpells.push_back(8690u);
|
||||
}
|
||||
knownSpells.insert(6603u);
|
||||
knownSpells.insert(8690u);
|
||||
|
||||
// Set initial cooldowns
|
||||
for (const auto& cd : data.cooldowns) {
|
||||
|
|
@ -7172,7 +7167,7 @@ void GameHandler::handleAuraUpdate(network::Packet& packet, bool isAll) {
|
|||
|
||||
void GameHandler::handleLearnedSpell(network::Packet& packet) {
|
||||
uint32_t spellId = packet.readUInt32();
|
||||
knownSpells.push_back(spellId);
|
||||
knownSpells.insert(spellId);
|
||||
LOG_INFO("Learned spell: ", spellId);
|
||||
|
||||
// Check if this spell corresponds to a talent rank
|
||||
|
|
@ -7192,9 +7187,7 @@ void GameHandler::handleLearnedSpell(network::Packet& packet) {
|
|||
|
||||
void GameHandler::handleRemovedSpell(network::Packet& packet) {
|
||||
uint32_t spellId = packet.readUInt32();
|
||||
knownSpells.erase(
|
||||
std::remove(knownSpells.begin(), knownSpells.end(), spellId),
|
||||
knownSpells.end());
|
||||
knownSpells.erase(spellId);
|
||||
LOG_INFO("Removed spell: ", spellId);
|
||||
}
|
||||
|
||||
|
|
@ -7204,12 +7197,10 @@ void GameHandler::handleSupercededSpell(network::Packet& packet) {
|
|||
uint32_t newSpellId = packet.readUInt32();
|
||||
|
||||
// Remove old spell
|
||||
knownSpells.erase(
|
||||
std::remove(knownSpells.begin(), knownSpells.end(), oldSpellId),
|
||||
knownSpells.end());
|
||||
knownSpells.erase(oldSpellId);
|
||||
|
||||
// Add new spell
|
||||
knownSpells.push_back(newSpellId);
|
||||
knownSpells.insert(newSpellId);
|
||||
|
||||
LOG_INFO("Spell superceded: ", oldSpellId, " -> ", newSpellId);
|
||||
|
||||
|
|
@ -7226,9 +7217,7 @@ void GameHandler::handleUnlearnSpells(network::Packet& packet) {
|
|||
|
||||
for (uint32_t i = 0; i < spellCount && packet.getSize() - packet.getReadPos() >= 4; ++i) {
|
||||
uint32_t spellId = packet.readUInt32();
|
||||
knownSpells.erase(
|
||||
std::remove(knownSpells.begin(), knownSpells.end(), spellId),
|
||||
knownSpells.end());
|
||||
knownSpells.erase(spellId);
|
||||
LOG_INFO(" Unlearned spell: ", spellId);
|
||||
}
|
||||
|
||||
|
|
@ -8300,8 +8289,8 @@ void GameHandler::handleTrainerList(network::Packet& packet) {
|
|||
}
|
||||
|
||||
// Check if specific prerequisite spells are known
|
||||
bool has527 = std::find(knownSpells.begin(), knownSpells.end(), 527u) != knownSpells.end();
|
||||
bool has25312 = std::find(knownSpells.begin(), knownSpells.end(), 25312u) != knownSpells.end();
|
||||
bool has527 = knownSpells.count(527u);
|
||||
bool has25312 = knownSpells.count(25312u);
|
||||
LOG_INFO("Prerequisite check: 527=", has527, " 25312=", has25312);
|
||||
|
||||
// Debug: log first few trainer spells to see their state
|
||||
|
|
|
|||
|
|
@ -4860,7 +4860,7 @@ void GameScreen::renderTrainerWindow(game::GameHandler& gameHandler) {
|
|||
auto isKnown = [&](uint32_t id) {
|
||||
if (id == 0) return true;
|
||||
// Check if spell is in knownSpells list
|
||||
bool found = std::find(knownSpells.begin(), knownSpells.end(), id) != knownSpells.end();
|
||||
bool found = knownSpells.count(id);
|
||||
if (found) return true;
|
||||
|
||||
// Also check if spell is in trainer list with state=2 (explicitly known)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ void SpellbookScreen::loadSkillLineDBCs(pipeline::AssetManager* assetManager) {
|
|||
}
|
||||
}
|
||||
|
||||
void SpellbookScreen::categorizeSpells(const std::vector<uint32_t>& knownSpells) {
|
||||
void SpellbookScreen::categorizeSpells(const std::unordered_set<uint32_t>& knownSpells) {
|
||||
spellTabs.clear();
|
||||
|
||||
// Only SkillLine category 7 ("Class") gets its own tab (the 3 specialties).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue