diff --git a/Common/UI/IconCache.cpp b/Common/UI/IconCache.cpp index a2697897d0..9c53aebad0 100644 --- a/Common/UI/IconCache.cpp +++ b/Common/UI/IconCache.cpp @@ -1,3 +1,5 @@ +#include + #include "Common/UI/IconCache.h" #include "Common/UI/Context.h" #include "Common/TimeUtil.h" @@ -5,9 +7,11 @@ #include "Common/Log.h" #define ICON_CACHE_VERSION 1 - #define MK_FOURCC(str) (str[0] | ((uint8_t)str[1] << 8) | ((uint8_t)str[2] << 16) | ((uint8_t)str[3] << 24)) +#define MAX_RUNTIME_CACHE_SIZE (1024 * 1024 * 4) +#define MAX_SAVED_CACHE_SIZE (1024 * 1024 * 1) + const uint32_t ICON_CACHE_MAGIC = MK_FOURCC("pICN"); IconCache g_iconCache; @@ -28,6 +32,9 @@ struct DiskCacheEntry { void IconCache::SaveToFile(FILE *file) { std::unique_lock lock(lock_); + // First, compute the total size. If above a threshold, remove until under. + Decimate(MAX_SAVED_CACHE_SIZE); + DiskCacheHeader header; header.magic = ICON_CACHE_MAGIC; header.version = ICON_CACHE_VERSION; @@ -131,6 +138,53 @@ void IconCache::FrameUpdate() { } lastUpdate_ = now; } + + if (now > lastDecimate_ + 60.0) { + Decimate(MAX_RUNTIME_CACHE_SIZE); + lastDecimate_ = now; + } +} + +void IconCache::Decimate(int64_t maxSize) { + // Call this under the lock. + + int64_t totalSize = 0; + for (auto &iter : cache_) { + totalSize += iter.second.data.size(); + } + + if (totalSize <= maxSize) { + return; + } + + // Create a list of all the entries, sort by date. Then delete until we reach the desired size. + struct SortEntry { + std::string key; + double usedTimestamp; + size_t size; + }; + + std::vector sortEntries; + for (auto iter : cache_) { + sortEntries.push_back({ iter.first, iter.second.usedTimeStamp, iter.second.data.size() }); + } + + std::sort(sortEntries.begin(), sortEntries.end(), [](const SortEntry &a, const SortEntry &b) { + // Oldest should be last in the lsit. + return a.usedTimestamp > b.usedTimestamp; + }); + + while (totalSize > maxSize && !sortEntries.empty()) { + totalSize -= sortEntries.back().size; + auto iter = cache_.find(sortEntries.back().key); + if (iter != cache_.end()) { + if (iter->second.texture) { + iter->second.texture->Release(); + } + cache_.erase(iter); + } + sortEntries.pop_back(); + } } bool IconCache::GetDimensions(const std::string &key, int *width, int *height) { diff --git a/Common/UI/IconCache.h b/Common/UI/IconCache.h index 0288b8de63..c6c6dc01bb 100644 --- a/Common/UI/IconCache.h +++ b/Common/UI/IconCache.h @@ -62,6 +62,8 @@ public: } private: + void Decimate(int64_t maxSize); + struct Entry { std::string data; IconFormat format; @@ -77,6 +79,7 @@ private: std::mutex lock_; double lastUpdate_ = 0.0; + double lastDecimate_ = 0.0; }; extern IconCache g_iconCache; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index b54d80140a..ff3489c80d 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -55,6 +55,7 @@ #include "Common/UI/Screen.h" #include "Common/UI/Context.h" #include "Common/UI/View.h" +#include "Common/UI/IconCache.h" #include "android/jni/app-android.h" @@ -774,6 +775,14 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch }); } + if (g_Config.bAchievementsEnable) { + FILE *iconCacheFile = File::OpenCFile(GetSysDirectory(DIRECTORY_CACHE) / "icon.cache", "rb"); + if (iconCacheFile) { + g_iconCache.LoadFromFile(iconCacheFile); + fclose(iconCacheFile); + } + } + DEBUG_LOG(SYSTEM, "ScreenManager!"); g_screenManager = new ScreenManager(); if (g_Config.memStickDirectory.empty()) { @@ -1367,6 +1376,14 @@ bool NativeIsRestarting() { void NativeShutdown() { Achievements::Shutdown(); + if (g_Config.bAchievementsEnable) { + FILE *iconCacheFile = File::OpenCFile(GetSysDirectory(DIRECTORY_CACHE) / "icon.cache", "wb"); + if (iconCacheFile) { + g_iconCache.SaveToFile(iconCacheFile); + fclose(iconCacheFile); + } + } + if (g_screenManager) { g_screenManager->shutdown(); delete g_screenManager;