diff --git a/include/rendering/character_renderer.hpp b/include/rendering/character_renderer.hpp index e324622e..2c73129a 100644 --- a/include/rendering/character_renderer.hpp +++ b/include/rendering/character_renderer.hpp @@ -264,7 +264,8 @@ private: std::unordered_map textureHasAlphaByPtr_; std::unordered_map textureColorKeyBlackByPtr_; std::unordered_map compositeCache_; // key → texture for reuse - std::unordered_set failedTextureCache_; // negative cache for missing textures + std::unordered_set failedTextureCache_; // negative cache for budget exhaustion + std::unordered_set loggedTextureLoadFails_; // dedup warning logs size_t textureCacheBytes_ = 0; uint64_t textureCacheCounter_ = 0; size_t textureCacheBudgetBytes_ = 1024ull * 1024 * 1024; diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index c9155a16..ad5b0868 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -526,17 +526,12 @@ VkTexture* CharacterRenderer::loadTexture(const std::string& path) { return whiteTexture_.get(); } - // Check negative cache to avoid repeated file I/O for textures that don't exist - if (failedTextureCache_.count(key)) { - return whiteTexture_.get(); - } - auto blpImage = assetManager->loadTexture(key); if (!blpImage.isValid()) { - static constexpr size_t kMaxFailedTextureCache = 200000; - core::Logger::getInstance().warning("Failed to load texture: ", path); - if (failedTextureCache_.size() < kMaxFailedTextureCache) { - failedTextureCache_.insert(key); + // Return white fallback but don't cache the failure — allow retry + // on next character load in case the asset becomes available. + if (loggedTextureLoadFails_.insert(key).second) { + core::Logger::getInstance().warning("Failed to load texture: ", path); } return whiteTexture_.get(); } diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 8d189950..f41390f3 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -3342,9 +3342,7 @@ VkTexture* M2Renderer::loadTexture(const std::string& path, uint32_t texFlags) { it->second.lastUse = ++textureCacheCounter_; return it->second.texture.get(); } - if (failedTextureCache_.count(key)) { - return whiteTexture_.get(); - } + // No negative cache check — allow retries for transiently missing textures auto containsToken = [](const std::string& haystack, const char* token) { return haystack.find(token) != std::string::npos; @@ -3365,10 +3363,8 @@ VkTexture* M2Renderer::loadTexture(const std::string& path, uint32_t texFlags) { // Load BLP texture pipeline::BLPImage blp = assetManager->loadTexture(key); if (!blp.isValid()) { - static constexpr size_t kMaxFailedTextureCache = 200000; - if (failedTextureCache_.size() < kMaxFailedTextureCache) { - failedTextureCache_.insert(key); - } + // Return white fallback but don't cache the failure — MPQ reads can + // fail transiently during streaming; allow retry on next model load. if (loggedTextureLoadFails_.insert(key).second) { LOG_WARNING("M2: Failed to load texture: ", path); } diff --git a/src/rendering/terrain_renderer.cpp b/src/rendering/terrain_renderer.cpp index 67770dfd..208ae25b 100644 --- a/src/rendering/terrain_renderer.cpp +++ b/src/rendering/terrain_renderer.cpp @@ -442,16 +442,10 @@ VkTexture* TerrainRenderer::loadTexture(const std::string& path) { it->second.lastUse = ++textureCacheCounter_; return it->second.texture.get(); } - if (failedTextureCache_.count(key)) { - return whiteTexture.get(); - } - pipeline::BLPImage blp = assetManager->loadTexture(key); if (!blp.isValid()) { - static constexpr size_t kMaxFailedTextureCache = 200000; - if (failedTextureCache_.size() < kMaxFailedTextureCache) { - failedTextureCache_.insert(key); - } + // Return white fallback but don't cache the failure — allow retry + // on next tile load in case the asset becomes available. if (loggedTextureLoadFails_.insert(key).second) { LOG_WARNING("Failed to load texture: ", path); }