GameInfoCache: Prioritize loads, so launching doesn't wait behind search

Type-to-search asks for the title of every game in the list, queueing a
load for each, and launching a game whose info wasn't loaded yet then
blocked until the whole queue had drained. Search's loads are now LOW,
and launching asks at HIGH, which queues its own load rather than wait
for a pending one.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-09-29 16:34:38 -06:00
1 parent ae460c9e1a
commit 3aaea4c0f9
4 files changed
+27 -10

No files matched your search

+2 -1
View File
@@ -473,7 +473,8 @@ void GameButton::Draw(UIContext &dc) {
}
std::string GameButton::DescribeText() const {
std::shared_ptr<GameInfo> 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<GameInfo> 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);
+16 -6
View File
@@ -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<GameInfo> &info, GameInfoFlags flags)
: gamePath_(gamePath), info_(info), flags_(flags) {}
GameInfoWorkItem(const Path &gamePath, std::shared_ptr<GameInfo> &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<GameInfo> 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<GameInfo> GameInfoCache::GetInfo(Draw::DrawContext *draw, const Path &gamePath, GameInfoFlags wantFlags, GameInfoFlags *outHasFlags, GameInfoFlags refetchFlags) {
std::shared_ptr<GameInfo> 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<GameInfo> 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<GameInfo> 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<GameInfo> 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;
}
+6 -1
View File
@@ -25,6 +25,7 @@
#include <condition_variable>
#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<GameInfo> 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<GameInfo> 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();
+3 -2
View File
@@ -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<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, path, neededFlags);
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, path, neededFlags, nullptr, GameInfoFlags::EMPTY, TaskPriority::HIGH);
info->WaitUntilReady(neededFlags);
switch (info->fileType) {