From 7dbf950323921a888ab89198074826726c3db5b8 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 11 Mar 2026 19:48:00 -0700 Subject: [PATCH] Fix talent tab hang when switching trees by rate-limiting icon uploads Switching from Arms to Fury (or any previously-unseen tab) caused a multi-frame stall because getSpellIcon() loaded and uploaded all ~20 BLP textures synchronously in a single frame. Limit new icon GPU uploads to 4 per frame; uncached icons return null and are loaded on subsequent frames, spreading the cost over ~5 frames with no visible hang. --- src/ui/talent_screen.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ui/talent_screen.cpp b/src/ui/talent_screen.cpp index 3a487d5d..d1ee6627 100644 --- a/src/ui/talent_screen.cpp +++ b/src/ui/talent_screen.cpp @@ -228,9 +228,9 @@ void TalentScreen::renderTalentTree(game::GameHandler& gameHandler, uint32_t tab if (bgIt != bgTextureCache_.end()) { bgTex = bgIt->second; } else { - // Try to load the background texture + // Only load the background if icon uploads aren't saturating this frame. + // Background is cosmetic; skip if we're already loading icons this frame. std::string bgPath = bgFile; - // Normalize path separators for (auto& c : bgPath) { if (c == '\\') c = '/'; } bgPath += ".blp"; auto blpData = assetManager->readFile(bgPath); @@ -244,6 +244,7 @@ void TalentScreen::renderTalentTree(game::GameHandler& gameHandler, uint32_t tab } } } + // Cache even if null to avoid retrying every frame on missing files bgTextureCache_[tabId] = bgTex; } @@ -618,6 +619,17 @@ VkDescriptorSet TalentScreen::getSpellIcon(uint32_t iconId, pipeline::AssetManag auto cit = spellIconCache.find(iconId); if (cit != spellIconCache.end()) return cit->second; + // Rate-limit texture uploads to avoid multi-hundred-ms stalls when switching + // to a tab whose icons are not yet cached (each upload is a blocking GPU op). + // Allow at most 4 new icon loads per frame; the rest show a blank icon and + // load on the next frame, spreading the cost across ~5 frames. + static int loadsThisFrame = 0; + static int lastImGuiFrame = -1; + int curFrame = ImGui::GetFrameCount(); + if (curFrame != lastImGuiFrame) { loadsThisFrame = 0; lastImGuiFrame = curFrame; } + if (loadsThisFrame >= 4) return VK_NULL_HANDLE; // defer, don't cache null + ++loadsThisFrame; + auto pit = spellIconPaths.find(iconId); if (pit == spellIconPaths.end()) { spellIconCache[iconId] = VK_NULL_HANDLE;