Kelsidavis-WoWee/include/pipeline/asset_manager.hpp
Kelsi 27d0496894 Add 1GB RAM cache for decompressed MPQ files
Eliminates repeated MPQ decompression overhead by caching decompressed
files in RAM with LRU eviction. Major performance improvement for file access.

Problem:
- Every readFile() call decompresses from MPQ (expensive!)
- M2 models, textures, WMO files decompressed repeatedly
- No caching of decompressed data
- MPQ decompression is CPU-intensive (zlib/bzip2)

Solution:
- Added 1GB LRU file cache to AssetManager
- Cache hit: instant return of decompressed data
- Cache miss: decompress once, cache for future access
- LRU eviction when cache full (removes least recently used)
- Don't cache files >100MB (avoid giant WMO chunks)
- Thread-safe with existing readMutex

Implementation:
- CachedFile struct: data + lastAccessTime
- fileCacheAccessCounter for LRU tracking
- Hit/miss statistics for monitoring
- Budget: 1GB (modern RAM easily handles this)

Performance impact:
- First load: same speed (decompress + cache)
- Subsequent loads: instant (no decompression)
- Expected 70-90% hit rate during normal play
- Huge benefit for frequently accessed models

Cache stats logged on shutdown to monitor effectiveness.
2026-02-08 22:37:29 -08:00

126 lines
3.2 KiB
C++

#pragma once
#include "pipeline/mpq_manager.hpp"
#include "pipeline/blp_loader.hpp"
#include "pipeline/dbc_loader.hpp"
#include <memory>
#include <string>
#include <map>
#include <mutex>
namespace wowee {
namespace pipeline {
/**
* AssetManager - Unified interface for loading WoW assets
*
* Coordinates MPQ archives, texture loading, and database files
*/
class AssetManager {
public:
AssetManager();
~AssetManager();
/**
* Initialize asset manager
* @param dataPath Path to WoW Data directory
* @return true if initialization succeeded
*/
bool initialize(const std::string& dataPath);
/**
* Shutdown and cleanup
*/
void shutdown();
/**
* Check if asset manager is initialized
*/
bool isInitialized() const { return initialized; }
/**
* Load a BLP texture
* @param path Virtual path to BLP file (e.g., "Textures\\Minimap\\Background.blp")
* @return BLP image (check isValid())
*/
BLPImage loadTexture(const std::string& path);
/**
* Load a DBC file
* @param name DBC file name (e.g., "Map.dbc")
* @return Loaded DBC file (check isLoaded())
*/
std::shared_ptr<DBCFile> loadDBC(const std::string& name);
/**
* Get a cached DBC file
* @param name DBC file name
* @return Cached DBC or nullptr if not loaded
*/
std::shared_ptr<DBCFile> getDBC(const std::string& name) const;
/**
* Check if a file exists in MPQ archives
* @param path Virtual file path
* @return true if file exists
*/
bool fileExists(const std::string& path) const;
/**
* Read raw file data from MPQ archives
* @param path Virtual file path
* @return File contents (empty if not found)
*/
std::vector<uint8_t> readFile(const std::string& path) const;
/**
* Get MPQ manager for direct access
*/
MPQManager& getMPQManager() { return mpqManager; }
const MPQManager& getMPQManager() const { return mpqManager; }
/**
* Get loaded DBC count
*/
size_t getLoadedDBCCount() const { return dbcCache.size(); }
/**
* Get file cache stats
*/
size_t getFileCacheSize() const { return fileCacheTotalBytes; }
size_t getFileCacheHits() const { return fileCacheHits; }
size_t getFileCacheMisses() const { return fileCacheMisses; }
/**
* Clear all cached resources
*/
void clearCache();
private:
bool initialized = false;
std::string dataPath;
MPQManager mpqManager;
mutable std::mutex readMutex;
std::map<std::string, std::shared_ptr<DBCFile>> dbcCache;
// Decompressed file cache (LRU, 1GB budget for modern RAM)
struct CachedFile {
std::vector<uint8_t> data;
uint64_t lastAccessTime;
};
mutable std::map<std::string, CachedFile> fileCache;
mutable size_t fileCacheTotalBytes = 0;
mutable uint64_t fileCacheAccessCounter = 0;
mutable size_t fileCacheHits = 0;
mutable size_t fileCacheMisses = 0;
static constexpr size_t FILE_CACHE_BUDGET = 1024 * 1024 * 1024; // 1GB
/**
* Normalize path for case-insensitive lookup
*/
std::string normalizePath(const std::string& path) const;
};
} // namespace pipeline
} // namespace wowee