diff --git a/UI/GameBrowser.cpp b/UI/GameBrowser.cpp index ef9be81dfb..6aea95b5d3 100644 --- a/UI/GameBrowser.cpp +++ b/UI/GameBrowser.cpp @@ -473,7 +473,8 @@ void GameButton::Draw(UIContext &dc) { } std::string GameButton::DescribeText() const { - std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); + // LOW: search calls this for every game in the list, which mustn't hold up the visible ones. + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO, nullptr, GameInfoFlags::EMPTY, TaskPriority::LOW); if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) return "..."; auto u = GetI18NCategory(I18NCat::UI_ELEMENTS); diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 20fa67ae97..0cde31802f 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -541,8 +541,8 @@ static bool LoadReplacementImage(GameInfo *info, GameInfoTex *tex, const char *f class GameInfoWorkItem : public Task { public: - GameInfoWorkItem(const Path &gamePath, std::shared_ptr &info, GameInfoFlags flags) - : gamePath_(gamePath), info_(info), flags_(flags) {} + GameInfoWorkItem(const Path &gamePath, std::shared_ptr &info, GameInfoFlags flags, TaskPriority priority) + : gamePath_(gamePath), info_(info), flags_(flags), priority_(priority) {} ~GameInfoWorkItem() { info_->DisposeFileLoader(); @@ -553,10 +553,14 @@ public: } TaskPriority Priority() const override { + if (priority_ == TaskPriority::HIGH) { + // Someone's waiting on this, wherever it is. + return TaskPriority::HIGH; + } switch (gamePath_.Type()) { case PathType::NATIVE: case PathType::CONTENT_URI: - return TaskPriority::NORMAL; + return priority_; default: // Remote/network access. @@ -997,6 +1001,7 @@ private: Path gamePath_; std::shared_ptr info_; GameInfoFlags flags_{}; + TaskPriority priority_; DISALLOW_COPY_AND_ASSIGN(GameInfoWorkItem); }; @@ -1110,7 +1115,7 @@ void GameInfoCache::PurgeType(IdentifiedFileType fileType) { // Call on the main thread ONLY - that is from stuff called from NativeFrame. // Can also be called from the audio thread for menu background music, but that cannot request images! -std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags, GameInfoFlags *outHasFlags, GameInfoFlags refetchFlags) { +std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags, GameInfoFlags *outHasFlags, GameInfoFlags refetchFlags, TaskPriority priority) { const std::string &pathStr = gamePath.ToString(); // _dbg_assert_(gamePath != GetSysDirectory(DIRECTORY_SAVEDATA)); @@ -1139,6 +1144,11 @@ std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const info->hasFlags &= ~refetchFlags; } GameInfoFlags willHaveFlags = info->hasFlags | info->pendingFlags; // We don't want to re-fetch data that we have, so or in pendingFlags. + if (priority == TaskPriority::HIGH) { + // A pending load may be sitting at the back of the queue (a search over a big list + // queues one per game), and the caller is about to block. Fetch it ourselves. + willHaveFlags = info->hasFlags; + } wanted = (GameInfoFlags)((int)wantFlags & ~(int)willHaveFlags); // & is reserved for testing so we have to cast to int. ugh. // FILE_TYPE is special: every work item switches on info->fileType, so it's not enough that // some *pending* item is going to compute it - that item may not have got there yet, and we'd @@ -1154,7 +1164,7 @@ std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const if (wanted != (GameInfoFlags)0) { // We're missing info that we want. Go get it! - GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wanted); + GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wanted, priority); g_threadManager.EnqueueTask(item); } return info; @@ -1170,7 +1180,7 @@ std::shared_ptr GameInfoCache::GetInfo(Draw::DrawContext *draw, const mapLock_.unlock(); // Just get all the stuff we wanted. - GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wantFlags); + GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info, wantFlags, priority); g_threadManager.EnqueueTask(item); return info; } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 50e4dde834..335dee8ea6 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -25,6 +25,7 @@ #include #include "Common/Thread/Event.h" +#include "Common/Thread/ThreadManager.h" #include "Core/ELF/ParamSFO.h" #include "Core/Util/PSARUnpack.h" #include "Common/File/Path.h" @@ -227,7 +228,11 @@ public: // because they're big. bgTextures and sound may be discarded over time as well. // NOTE: This never returns null, so you don't need to check for that. Do check Ready() flags though. // It's OK to pass in nullptr for draw if you don't need the actual texture right now. - std::shared_ptr GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags, GameInfoFlags *outHasFlags = nullptr, GameInfoFlags refetchFlags = GameInfoFlags::EMPTY); + // priority orders the background loads: NORMAL for what's on screen, LOW for speculative work like + // search. HIGH is for a one-off request you're about to block on: it doesn't wait behind a + // lower-priority load that's already queued for the same data, it queues its own - so don't use it + // every frame. + std::shared_ptr GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags, GameInfoFlags *outHasFlags = nullptr, GameInfoFlags refetchFlags = GameInfoFlags::EMPTY, TaskPriority priority = TaskPriority::NORMAL); void FlushBGs(); // Gets rid of all BG textures. Also gets rid of bg sounds. void CancelAll(); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index cf84b31024..6d32ca3a8a 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -72,9 +72,10 @@ static void LaunchFile(ScreenManager *screenManager, Screen *currentScreen, cons // Check if we already know that this game isn't playable. // If coming from the main screen, the info will already be computed here since the icon is displayed etc. // Otherwise (launching from a file association, a shortcut, drag-and-drop...) we have to block until - // it's available - we can't decide what to do below without it. + // it's available - we can't decide what to do below without it. HIGH, so we don't wait behind + // whatever else is queued, like a search's loads for every game in a big list. const GameInfoFlags neededFlags = GameInfoFlags::FILE_TYPE | GameInfoFlags::PARAM_SFO; - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, path, neededFlags); + std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, path, neededFlags, nullptr, GameInfoFlags::EMPTY, TaskPriority::HIGH); info->WaitUntilReady(neededFlags); switch (info->fileType) {