From b5d5cfd9c225755df49ac45f23dcd61b448715de Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 4 Sep 2025 19:32:25 +1000 Subject: [PATCH] GameList: Pack state in struct --- src/core/game_database.cpp | 4 +- src/core/game_list.cpp | 157 ++++++++++++++++-------------- src/core/performance_counters.cpp | 4 +- 3 files changed, 86 insertions(+), 79 deletions(-) diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index 90b2865ba..9eee7e919 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -164,7 +164,7 @@ static constexpr const char* DISCDB_YAML_FILENAME = "discdb.yaml"; static DisplayDeinterlacingMode DEFAULT_DEINTERLACING_MODE = DisplayDeinterlacingMode::Adaptive; namespace { -struct ALIGN_TO_CACHE_LINE State +struct State { bool loaded; bool track_hashes_loaded; @@ -182,7 +182,7 @@ struct ALIGN_TO_CACHE_LINE State }; } // namespace -static State s_state; +ALIGN_TO_CACHE_LINE static State s_state; } // namespace GameDatabase diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index c3876ff12..7f0307ca1 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -136,12 +136,18 @@ static bool PutCustomPropertiesField(INISettingsInterface& ini, const std::strin static FileSystem::ManagedCFilePtr OpenMemoryCardTimestampCache(bool for_write); static bool UpdateMemcardTimestampCache(const MemcardTimestampCacheEntry& entry); -static EntryList s_entries; -static std::recursive_mutex s_mutex; -static CacheMap s_cache_map; -static std::vector s_memcard_timestamp_cache_entries; +struct State +{ + ; + EntryList entries; + std::recursive_mutex mutex; + CacheMap cache_map; + std::vector memcard_timestamp_cache_entries; + + bool game_list_loaded = false; +}; -static bool s_game_list_loaded = false; +ALIGN_TO_CACHE_LINE static State s_state; } // namespace GameList @@ -171,7 +177,7 @@ const char* GameList::GetEntryTypeDisplayName(EntryType type) bool GameList::IsGameListLoaded() { - return s_game_list_loaded; + return s_state.game_list_loaded; } bool GameList::ShouldShowLocalizedTitles() @@ -413,13 +419,13 @@ bool GameList::GetGameListEntryFromCache(const std::string& path, Entry* entry, const INISettingsInterface& custom_attributes_ini, const Achievements::ProgressDatabase& achievements_progress) { - auto iter = s_cache_map.find(path); - if (iter == s_cache_map.end()) + auto iter = s_state.cache_map.find(path); + if (iter == s_state.cache_map.end()) return false; *entry = std::move(iter->second); entry->dbentry = GameDatabase::GetEntryForSerial(entry->serial); - s_cache_map.erase(iter); + s_state.cache_map.erase(iter); ApplyCustomAttributes(path, entry, custom_attributes_ini); if (entry->IsDisc()) PopulateEntryAchievements(entry, achievements_progress); @@ -460,11 +466,11 @@ bool GameList::LoadEntriesFromCache(BinaryFileReader& reader) ge.region = static_cast(region); ge.type = static_cast(type); - auto iter = s_cache_map.find(ge.path); - if (iter != s_cache_map.end()) + auto iter = s_state.cache_map.find(ge.path); + if (iter != s_state.cache_map.end()) iter->second = std::move(ge); else - s_cache_map.emplace(std::move(path), std::move(ge)); + s_state.cache_map.emplace(std::move(path), std::move(ge)); } return true; @@ -496,7 +502,7 @@ bool GameList::LoadOrInitializeCache(std::FILE* fp, bool invalidate_cache) } WARNING_LOG("Initializing game list cache."); - s_cache_map.clear(); + s_state.cache_map.clear(); if (!fp) return false; @@ -572,7 +578,7 @@ void GameList::ScanDirectory(const std::string& path, bool recursive, bool only_ ffd.FileName = Path::Combine(EmuFolders::DataRoot, path_in_cache); } - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); if (GetEntryForPath(ffd.FileName) || AddFileFromCache(ffd.FileName, path_in_cache, ffd.ModificationTime, played_time_map, custom_attributes_ini, achievements_progress) || @@ -619,7 +625,7 @@ bool GameList::AddFileFromCache(const std::string& path, const std::string& path if (!path_in_cache.empty()) entry.path = path; - s_entries.push_back(std::move(entry)); + s_state.entries.push_back(std::move(entry)); return true; } @@ -670,12 +676,12 @@ void GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc return; // replace if present - auto it = std::find_if(s_entries.begin(), s_entries.end(), + auto it = std::find_if(s_state.entries.begin(), s_state.entries.end(), [&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); }); - if (it != s_entries.end()) + if (it != s_state.entries.end()) *it = std::move(entry); else - s_entries.push_back(std::move(entry)); + s_state.entries.push_back(std::move(entry)); } bool GameList::RescanCustomAttributesForPath(const std::string& path, const INISettingsInterface& custom_attributes_ini) @@ -708,15 +714,15 @@ bool GameList::RescanCustomAttributesForPath(const std::string& path, const INIS ApplyCustomAttributes(entry.path, &entry, custom_attributes_ini); - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); // replace if present - auto it = std::find_if(s_entries.begin(), s_entries.end(), + auto it = std::find_if(s_state.entries.begin(), s_state.entries.end(), [&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); }); - if (it != s_entries.end()) + if (it != s_state.entries.end()) *it = std::move(entry); else - s_entries.push_back(std::move(entry)); + s_state.entries.push_back(std::move(entry)); return true; } @@ -789,12 +795,12 @@ void GameList::PopulateEntryAchievements(Entry* entry, const Achievements::Progr void GameList::UpdateAchievementData(const std::span hash, u32 game_id, u32 num_achievements, u32 num_unlocked, u32 num_unlocked_hardcore) { - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); llvm::SmallVector changed_indices; - for (size_t i = 0; i < s_entries.size(); i++) + for (size_t i = 0; i < s_state.entries.size(); i++) { - Entry& entry = s_entries[i]; + Entry& entry = s_state.entries[i]; if (std::memcmp(entry.achievements_hash.data(), hash.data(), hash.size()) != 0 && entry.achievements_game_id != game_id) { @@ -829,13 +835,13 @@ void GameList::UpdateAllAchievementData() WARNING_LOG("Failed to load achievements progress: {}", error.GetDescription()); } - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); // this is pretty jank, but the frontend should collapse it into a single update std::vector changed_indices; - for (size_t i = 0; i < s_entries.size(); i++) + for (size_t i = 0; i < s_state.entries.size(); i++) { - Entry& entry = s_entries[i]; + Entry& entry = s_state.entries[i]; if (!entry.IsDisc()) continue; @@ -855,9 +861,9 @@ void GameList::UpdateAllAchievementData() } // and now the disc sets, messier :( - for (size_t i = 0; i < s_entries.size(); i++) + for (size_t i = 0; i < s_state.entries.size(); i++) { - Entry& entry = s_entries[i]; + Entry& entry = s_state.entries[i]; if (!entry.IsDiscSet()) continue; @@ -884,17 +890,17 @@ void GameList::UpdateAllAchievementData() std::unique_lock GameList::GetLock() { - return std::unique_lock(s_mutex); + return std::unique_lock(s_state.mutex); } std::span GameList::GetEntries() { - return s_entries; + return s_state.entries; } const GameList::Entry* GameList::GetEntryByIndex(u32 index) { - return (index < s_entries.size()) ? &s_entries[index] : nullptr; + return (index < s_state.entries.size()) ? &s_state.entries[index] : nullptr; } const GameList::Entry* GameList::GetEntryForPath(std::string_view path) @@ -904,7 +910,7 @@ const GameList::Entry* GameList::GetEntryForPath(std::string_view path) GameList::Entry* GameList::GetMutableEntryForPath(std::string_view path) { - for (Entry& entry : s_entries) + for (Entry& entry : s_state.entries) { // Use case-insensitive compare on Windows, since it's the same file. #ifdef _WIN32 @@ -923,7 +929,7 @@ const GameList::Entry* GameList::GetEntryBySerial(std::string_view serial) { const Entry* fallback_entry = nullptr; - for (const Entry& entry : s_entries) + for (const Entry& entry : s_state.entries) { if (!entry.IsDiscSet() && entry.serial == serial) { @@ -942,7 +948,7 @@ const GameList::Entry* GameList::GetEntryBySerialAndHash(std::string_view serial { const Entry* fallback_entry = nullptr; - for (const Entry& entry : s_entries) + for (const Entry& entry : s_state.entries) { if (!entry.IsDiscSet() && entry.serial == serial && entry.hash == hash) { @@ -963,7 +969,7 @@ std::vector GameList::GetDiscSetMembers(const GameDataba Assert(dsentry); std::vector ret; - for (const Entry& entry : s_entries) + for (const Entry& entry : s_state.entries) { if (!entry.disc_set_member || !entry.dbentry || entry.dbentry->disc_set != dsentry) continue; @@ -993,7 +999,7 @@ const GameList::Entry* GameList::GetFirstDiscSetMember(const GameDatabase::DiscS { Assert(dsentry); - for (const Entry& entry : s_entries) + for (const Entry& entry : s_state.entries) { if (!entry.disc_set_member || !entry.dbentry || entry.dbentry->disc_set != dsentry) continue; @@ -1008,12 +1014,12 @@ const GameList::Entry* GameList::GetFirstDiscSetMember(const GameDatabase::DiscS u32 GameList::GetEntryCount() { - return static_cast(s_entries.size()); + return static_cast(s_state.entries.size()); } void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* progress /* = nullptr */) { - s_game_list_loaded = true; + s_state.game_list_loaded = true; if (!progress) progress = ProgressCallback::NullProgressCallback; @@ -1043,8 +1049,8 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* // don't delete the old entries, since the frontend might still access them std::vector old_entries; { - std::unique_lock lock(s_mutex); - old_entries.swap(s_entries); + std::unique_lock lock(s_state.mutex); + old_entries.swap(s_state.entries); } const std::vector excluded_paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths")); @@ -1093,7 +1099,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* } // don't need unused cache entries - s_cache_map.clear(); + s_state.cache_map.clear(); // merge multi-disc games CreateDiscSetEntries(excluded_paths, played_time); @@ -1101,19 +1107,19 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* GameList::EntryList GameList::TakeEntryList() { - EntryList ret = std::move(s_entries); - s_entries = {}; + EntryList ret = std::move(s_state.entries); + s_state.entries = {}; return ret; } void GameList::CreateDiscSetEntries(const std::vector& excluded_paths, const PlayedTimeMap& played_time_map) { - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); - for (size_t i = 0; i < s_entries.size(); i++) + for (size_t i = 0; i < s_state.entries.size(); i++) { - const Entry& entry = s_entries[i]; + const Entry& entry = s_state.entries[i]; // only first discs can create sets if (entry.type != EntryType::Disc || !entry.dbentry || entry.disc_set_member || entry.disc_set_index != 0) @@ -1122,7 +1128,7 @@ void GameList::CreateDiscSetEntries(const std::vector& excluded_pat // need at least two discs for a set const GameDatabase::DiscSetEntry* dsentry = entry.dbentry->disc_set; bool found_another_disc = false; - for (const Entry& other_entry : s_entries) + for (const Entry& other_entry : s_state.entries) { if (other_entry.type != EntryType::Disc || other_entry.disc_set_member || !other_entry.dbentry || other_entry.dbentry->disc_set != dsentry || other_entry.disc_set_index == entry.disc_set_index) @@ -1173,7 +1179,7 @@ void GameList::CreateDiscSetEntries(const std::vector& excluded_pat // mark all discs for this set as part of it, so we don't try to add them again, and for filtering u32 num_parts = 0; - for (Entry& other_entry : s_entries) + for (Entry& other_entry : s_state.entries) { if (other_entry.type != EntryType::Disc || other_entry.disc_set_member || !other_entry.dbentry || other_entry.dbentry->disc_set != dsentry) @@ -1193,7 +1199,7 @@ void GameList::CreateDiscSetEntries(const std::vector& excluded_pat // we have to do the exclusion check at the end, because otherwise the individual discs get added if (!IsPathExcluded(excluded_paths, dsentry->title)) - s_entries.push_back(std::move(set_entry)); + s_state.entries.push_back(std::move(set_entry)); } } @@ -1488,13 +1494,13 @@ void GameList::AddPlayedTimeForSerial(const std::string& serial, std::time_t las VERBOSE_LOG("Add {} seconds play time to {} -> now {}", static_cast(add_time), serial.c_str(), static_cast(pt.total_played_time)); - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); const GameDatabase::Entry* dbentry = GameDatabase::GetEntryForSerial(serial); llvm::SmallVector changed_indices; - for (size_t i = 0; i < s_entries.size(); i++) + for (size_t i = 0; i < s_state.entries.size(); i++) { - Entry& entry = s_entries[i]; + Entry& entry = s_state.entries[i]; if (entry.IsDisc()) { if (entry.serial != serial) @@ -1527,8 +1533,8 @@ void GameList::ClearPlayedTimeForSerial(const std::string& serial) UpdatePlayedTimeFile(GetPlayedTimeFile(), serial, 0, 0); - std::unique_lock lock(s_mutex); - for (GameList::Entry& entry : s_entries) + std::unique_lock lock(s_state.mutex); + for (GameList::Entry& entry : s_state.entries) { if (entry.serial != serial) continue; @@ -1540,7 +1546,7 @@ void GameList::ClearPlayedTimeForSerial(const std::string& serial) void GameList::ClearPlayedTimeForEntry(const GameList::Entry* entry) { - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); std::vector serials; if (entry->IsDiscSet()) @@ -1564,7 +1570,7 @@ void GameList::ClearPlayedTimeForEntry(const GameList::Entry* entry) UpdatePlayedTimeFile(played_time_file, serial, 0, 0); } - for (GameList::Entry& list_entry : s_entries) + for (GameList::Entry& list_entry : s_state.entries) { if (std::find(serials.begin(), serials.end(), list_entry.serial) == serials.end()) continue; @@ -1579,8 +1585,8 @@ std::time_t GameList::GetCachedPlayedTimeForSerial(const std::string& serial) if (serial.empty()) return 0; - std::unique_lock lock(s_mutex); - for (GameList::Entry& entry : s_entries) + std::unique_lock lock(s_state.mutex); + for (GameList::Entry& entry : s_state.entries) { if (entry.serial == serial) return entry.total_played_time; @@ -1672,7 +1678,7 @@ GameList::GetEntriesInDiscSet(const GameDatabase::DiscSetEntry* dsentry, bool lo const Entry* matching_entry = nullptr; bool has_multiple_entries = false; - for (const Entry& entry : s_entries) + for (const Entry& entry : s_state.entries) { if (entry.IsDiscSet() || entry.serial != serial) continue; @@ -1693,7 +1699,7 @@ GameList::GetEntriesInDiscSet(const GameDatabase::DiscSetEntry* dsentry, bool lo } // Have to add all matching files. - for (const Entry& entry : s_entries) + for (const Entry& entry : s_state.entries) { if (entry.IsDiscSet() || entry.serial != serial) continue; @@ -1738,8 +1744,8 @@ bool GameList::DownloadCovers(const std::vector& url_templates, boo std::vector> download_urls; { - std::unique_lock lock(s_mutex); - for (const GameList::Entry& entry : s_entries) + std::unique_lock lock(s_state.mutex); + for (const GameList::Entry& entry : s_state.entries) { const std::string existing_path(GetCoverImagePathForEntry(&entry)); if (!existing_path.empty()) @@ -1791,7 +1797,7 @@ bool GameList::DownloadCovers(const std::vector& url_templates, boo // make sure it didn't get done already { - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); const GameList::Entry* entry = GetEntryForPath(entry_path); if (!entry || !GetCoverImagePathForEntry(entry).empty()) { @@ -1814,7 +1820,7 @@ bool GameList::DownloadCovers(const std::vector& url_templates, boo return; } - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); const GameList::Entry* entry = GetEntryForPath(entry_path); if (!entry || !GetCoverImagePathForEntry(entry).empty()) return; @@ -1971,7 +1977,7 @@ std::string GameList::GetCustomTitleForPath(const std::string_view path) { std::string ret; - std::unique_lock lock(s_mutex); + std::unique_lock lock(s_state.mutex); const GameList::Entry* entry = GetEntryForPath(path); if (entry && entry->has_custom_title) ret = entry->title; @@ -2039,7 +2045,7 @@ FileSystem::ManagedCFilePtr GameList::OpenMemoryCardTimestampCache(bool for_writ void GameList::ReloadMemcardTimestampCache() { - s_memcard_timestamp_cache_entries.clear(); + s_state.memcard_timestamp_cache_entries.clear(); FileSystem::ManagedCFilePtr fp = OpenMemoryCardTimestampCache(false); if (!fp) @@ -2065,16 +2071,17 @@ void GameList::ReloadMemcardTimestampCache() return; } - s_memcard_timestamp_cache_entries.resize(static_cast(count)); - if (std::fread(s_memcard_timestamp_cache_entries.data(), sizeof(MemcardTimestampCacheEntry), - s_memcard_timestamp_cache_entries.size(), fp.get()) != s_memcard_timestamp_cache_entries.size()) + s_state.memcard_timestamp_cache_entries.resize(static_cast(count)); + if (std::fread(s_state.memcard_timestamp_cache_entries.data(), sizeof(MemcardTimestampCacheEntry), + s_state.memcard_timestamp_cache_entries.size(), + fp.get()) != s_state.memcard_timestamp_cache_entries.size()) { - s_memcard_timestamp_cache_entries = {}; + s_state.memcard_timestamp_cache_entries = {}; return; } // Just in case. - for (MemcardTimestampCacheEntry& entry : s_memcard_timestamp_cache_entries) + for (MemcardTimestampCacheEntry& entry : s_state.memcard_timestamp_cache_entries) entry.serial[sizeof(entry.serial) - 1] = 0; } @@ -2106,7 +2113,7 @@ std::string GameList::GetGameIconPath(std::string_view serial, std::string_view serial.substr(0, std::min(serial.length(), MemcardTimestampCacheEntry::MAX_SERIAL_LENGTH - 1))); MemcardTimestampCacheEntry* serial_entry = nullptr; - for (MemcardTimestampCacheEntry& entry : s_memcard_timestamp_cache_entries) + for (MemcardTimestampCacheEntry& entry : s_state.memcard_timestamp_cache_entries) { if (StringUtil::EqualNoCase(index_serial, entry.serial)) { @@ -2125,7 +2132,7 @@ std::string GameList::GetGameIconPath(std::string_view serial, std::string_view if (!serial_entry) { - serial_entry = &s_memcard_timestamp_cache_entries.emplace_back(); + serial_entry = &s_state.memcard_timestamp_cache_entries.emplace_back(); std::memset(serial_entry, 0, sizeof(MemcardTimestampCacheEntry)); } diff --git a/src/core/performance_counters.cpp b/src/core/performance_counters.cpp index c3d79fba0..51a1a234d 100644 --- a/src/core/performance_counters.cpp +++ b/src/core/performance_counters.cpp @@ -22,7 +22,7 @@ namespace PerformanceCounters { namespace { -struct ALIGN_TO_CACHE_LINE State +struct State { Timer::Value last_update_time; Timer::Value last_frame_time; @@ -63,7 +63,7 @@ struct ALIGN_TO_CACHE_LINE State static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f; -State s_state = {}; +ALIGN_TO_CACHE_LINE State s_state = {}; } // namespace PerformanceCounters