diff --git a/Client/mods/deathmatch/logic/CClientManager.cpp b/Client/mods/deathmatch/logic/CClientManager.cpp index e3a808555b..e5caae94ce 100644 --- a/Client/mods/deathmatch/logic/CClientManager.cpp +++ b/Client/mods/deathmatch/logic/CClientManager.cpp @@ -218,6 +218,7 @@ void CClientManager::DoPulse(bool bDoStandardPulses, bool bDoVehicleManagerPulse m_pColManager->DoPulse(); m_pGUIManager->DoPulse(); m_pWeaponManager->DoPulse(); + m_pRenderElementManager->DoPulse(); } else { diff --git a/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp b/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp index 7cda0784e5..a7cb69a155 100644 --- a/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp +++ b/Client/mods/deathmatch/logic/CClientRenderElementManager.cpp @@ -11,6 +11,10 @@ #include "StdInc.h" #include "CClientVectorGraphic.h" +constexpr std::int64_t TEMPORARY_TEXTURES_CLEANUP_PERIOD = 5000ll; +constexpr std::int64_t TEMPORARY_TEXTURES_CLEANUP_THRESHOLD = 10000ll; +constexpr std::size_t TEMPORARY_TEXTURES_DELETE_THRESHOLD = 10u; + //////////////////////////////////////////////////////////////// // // CClientRenderElementManager::CClientRenderElementManager @@ -283,20 +287,24 @@ CClientVectorGraphic* CClientRenderElementManager::CreateVectorGraphic(uint widt CClientTexture* CClientRenderElementManager::FindAutoTexture(const SString& strFullFilePath, const SString& strUniqueName) { // Check if we've already done this file - CClientTexture** ppTextureElement = MapFind(m_AutoTextureMap, strUniqueName); + SAutoTexture* ppTextureElement = MapFind(m_AutoTextureMap, strUniqueName); if (!ppTextureElement) { // Try to create CClientTexture* pNewTextureElement = CreateTexture(strFullFilePath); if (!pNewTextureElement) - return NULL; + return nullptr; + + pNewTextureElement->MakeSystemEntity(); // Add to automap if created - MapSet(m_AutoTextureMap, strUniqueName, pNewTextureElement); + MapSet(m_AutoTextureMap, strUniqueName, SAutoTexture{pNewTextureElement}); ppTextureElement = MapFind(m_AutoTextureMap, strUniqueName); } - return *ppTextureElement; + ppTextureElement->lastUse = CTickCount::Now(); + + return ppTextureElement->texture; } //////////////////////////////////////////////////////////////// @@ -318,9 +326,9 @@ void CClientRenderElementManager::Remove(CClientRenderElement* pElement) // Remove from auto texture map if (pElement->IsA(CClientTexture::GetClassId())) { - for (std::map::iterator iter = m_AutoTextureMap.begin(); iter != m_AutoTextureMap.end(); ++iter) + for (auto iter = m_AutoTextureMap.begin(); iter != m_AutoTextureMap.end(); ++iter) { - if (iter->second == pElement) + if (iter->second.texture == pElement) { m_AutoTextureMap.erase(iter); break; @@ -350,3 +358,39 @@ void CClientRenderElementManager::Remove(CClientRenderElement* pElement) CRenderItem* pRenderItem = pElement->GetRenderItem(); SAFE_RELEASE(pRenderItem); } + +void CClientRenderElementManager::DoPulse() +{ + if (m_texturePulseTimer.Get() < TEMPORARY_TEXTURES_CLEANUP_PERIOD) + return; + + m_texturePulseTimer.Reset(); + + const CTickCount now = CTickCount::Now(); + + std::vector deleteCandidates; + deleteCandidates.reserve(TEMPORARY_TEXTURES_DELETE_THRESHOLD); + + for (const auto& [texName, texInfo] : m_AutoTextureMap) + { + const std::int64_t timeElapsedMs = (now - texInfo.lastUse).ToLongLong(); + if (timeElapsedMs < TEMPORARY_TEXTURES_CLEANUP_THRESHOLD) + continue; + + CTextureItem* textureItem = texInfo.texture->GetTextureItem(); + if (textureItem && textureItem->m_iRefCount > 1) + continue; + + // CElementDeleter::Delete causes changes in m_AutoTextureMap + // and we cannot delete a texture while iterating over it + deleteCandidates.push_back(texInfo.texture); + + // The deletion procedure can be expensive + // and we're interested in capping on the number of deleted texture at once + if (deleteCandidates.size() == TEMPORARY_TEXTURES_DELETE_THRESHOLD) + break; + } + + for (CClientTexture* texture : deleteCandidates) + g_pClientGame->GetElementDeleter()->Delete(texture); +} diff --git a/Client/mods/deathmatch/logic/CClientRenderElementManager.h b/Client/mods/deathmatch/logic/CClientRenderElementManager.h index ff305cef67..6f188d29ef 100644 --- a/Client/mods/deathmatch/logic/CClientRenderElementManager.h +++ b/Client/mods/deathmatch/logic/CClientRenderElementManager.h @@ -39,6 +39,8 @@ class CClientRenderElementManager CClientTexture* FindAutoTexture(const SString& strFullFilePath, const SString& strUniqueName); void Remove(CClientRenderElement* pElement); + void DoPulse(); + uint GetDxFontCount() { return m_uiStatsDxFontCount; } uint GetGuiFontCount() { return m_uiStatsGuiFontCount; } uint GetTextureCount() { return m_uiStatsTextureCount; } @@ -49,9 +51,16 @@ class CClientRenderElementManager uint GetVectorGraphicCount() { return m_uiStatsVectorGraphicCount; } protected: + struct SAutoTexture + { + CClientTexture* texture{}; + CTickCount lastUse; + }; + + CElapsedTime m_texturePulseTimer; CClientManager* m_pClientManager; CRenderItemManagerInterface* m_pRenderItemManager; - std::map m_AutoTextureMap; + std::map m_AutoTextureMap; std::map m_ItemElementMap; uint m_uiStatsGuiFontCount; uint m_uiStatsDxFontCount;