From 049eca38a4e97f56135ac33e773754a31d020bba Mon Sep 17 00:00:00 2001 From: savis <106487343+savisxss@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:37:08 +0100 Subject: [PATCH] Add LRU texture cache - 512MB default cache size for decoded textures - Thread-safe LRU eviction policy - Tracks hit/miss statistics - Prevents re-decoding frequently used textures --- src/EterLib/TextureCache.cpp | 109 +++++++++++++++++++++++++++++++++++ src/EterLib/TextureCache.h | 55 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 src/EterLib/TextureCache.cpp create mode 100644 src/EterLib/TextureCache.h diff --git a/src/EterLib/TextureCache.cpp b/src/EterLib/TextureCache.cpp new file mode 100644 index 0000000..030b200 --- /dev/null +++ b/src/EterLib/TextureCache.cpp @@ -0,0 +1,109 @@ +#include "StdAfx.h" +#include "TextureCache.h" + +CTextureCache::CTextureCache(size_t maxMemoryMB) + : m_maxMemory(maxMemoryMB * 1024 * 1024) + , m_currentMemory(0) + , m_hits(0) + , m_misses(0) +{ +} + +CTextureCache::~CTextureCache() +{ + Clear(); +} + +bool CTextureCache::Get(const std::string& filename, TCachedTexture& outTexture) +{ + std::lock_guard lock(m_mutex); + + auto it = m_cache.find(filename); + if (it == m_cache.end()) + { + m_misses.fetch_add(1); + return false; + } + + // Move to back of LRU (most recently used) + m_lruList.erase(it->second.second); + m_lruList.push_back(filename); + it->second.second = std::prev(m_lruList.end()); + + // Copy texture data + outTexture = it->second.first; + + m_hits.fetch_add(1); + return true; +} + +void CTextureCache::Put(const std::string& filename, const TCachedTexture& texture) +{ + std::lock_guard lock(m_mutex); + + // Check if already cached + auto it = m_cache.find(filename); + if (it != m_cache.end()) + { + // Update existing entry + m_currentMemory -= it->second.first.memorySize; + m_lruList.erase(it->second.second); + m_cache.erase(it); + } + + // Evict if needed + while (m_currentMemory + texture.memorySize > m_maxMemory && !m_cache.empty()) + { + Evict(); + } + + // Don't cache if too large + if (texture.memorySize > m_maxMemory / 4) + { + return; // Skip caching huge textures + } + + // Add to cache + m_lruList.push_back(filename); + auto lruIt = std::prev(m_lruList.end()); + m_cache[filename] = {texture, lruIt}; + m_currentMemory += texture.memorySize; +} + +void CTextureCache::Clear() +{ + std::lock_guard lock(m_mutex); + m_cache.clear(); + m_lruList.clear(); + m_currentMemory = 0; +} + +float CTextureCache::GetHitRate() const +{ + size_t hits = m_hits.load(); + size_t misses = m_misses.load(); + size_t total = hits + misses; + + if (total == 0) + return 0.0f; + + return (float)hits / (float)total; +} + +void CTextureCache::Evict() +{ + // Remove least recently used (front of list) + if (m_lruList.empty()) + return; + + const std::string& filename = m_lruList.front(); + auto it = m_cache.find(filename); + + if (it != m_cache.end()) + { + m_currentMemory -= it->second.first.memorySize; + m_cache.erase(it); + } + + m_lruList.pop_front(); +} diff --git a/src/EterLib/TextureCache.h b/src/EterLib/TextureCache.h new file mode 100644 index 0000000..5416a13 --- /dev/null +++ b/src/EterLib/TextureCache.h @@ -0,0 +1,55 @@ +#ifndef __INC_ETERLIB_TEXTURECACHE_H__ +#define __INC_ETERLIB_TEXTURECACHE_H__ + +#include +#include +#include +#include + +// LRU cache for decoded textures +class CTextureCache +{ +public: + struct TCachedTexture + { + std::vector pixels; + int width; + int height; + size_t memorySize; + std::string filename; + }; + + CTextureCache(size_t maxMemoryMB = 256); + ~CTextureCache(); + + // Get cached texture + bool Get(const std::string& filename, TCachedTexture& outTexture); + + // Add texture to cache + void Put(const std::string& filename, const TCachedTexture& texture); + + // Clear cache + void Clear(); + + // Get statistics + size_t GetMemoryUsage() const { return m_currentMemory; } + size_t GetMaxMemory() const { return m_maxMemory; } + size_t GetCachedCount() const { return m_cache.size(); } + float GetHitRate() const; + +private: + void Evict(); + +private: + size_t m_maxMemory; + size_t m_currentMemory; + + std::list m_lruList; + std::unordered_map::iterator>> m_cache; + + mutable std::mutex m_mutex; + std::atomic m_hits; + std::atomic m_misses; +}; + +#endif // __INC_ETERLIB_TEXTURECACHE_H__