aboutsummaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 04:27:49 -0500
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 08:39:38 -0500
commitf8813998e7ddc1b21b42dcf51c39943ceb83b987 (patch)
tree249eb2843735172b1a171d55622ac611e3de6bd9 /src/core/loader
parentadae7635708e155d967e84d7ad84bf63c5203409 (diff)
loader: build a loose RomFS once and only when it is read
Every loader of an extracted title built the whole RomFS from its romfs/ directory in its constructor, though most are made only for metadata (the game list, title lookups), and each build keeps an object per file: title 1's 206k files left four copies alive, ~0.6 GB of the heap. The RomFS is now built on first read, and loaders of the same directory share one build while any of them holds it. (cherry picked from commit be47212cac130e6a895423644751c0525dc6f169)
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp34
-rw-r--r--src/core/loader/deconstructed_rom_directory.h5
2 files changed, 34 insertions, 5 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index cda1645548..04b05d188f 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -7,6 +7,9 @@
#include <algorithm>
#include <cctype>
#include <cstring>
+#include <map>
+#include <memory>
+#include <mutex>
#include <string_view>
#include "common/logging.h"
#include "common/random.h"
@@ -90,8 +93,19 @@ FileSys::VirtualFile FindLooseRomFS(const FileSys::VirtualDir& exefs_dir) {
}
if (const auto parent = exefs_dir->GetParentDirectory()) {
if (const auto romfs_dir = parent->GetSubdirectory("romfs")) {
+ // Building it walks every file and keeps an object for each (title 1: 206k files, ~150
+ // MiB), so loaders of the same directory share one build while any of them holds it.
+ static std::mutex cache_mutex;
+ static std::map<std::string, std::weak_ptr<FileSys::VfsFile>> cache;
+ std::scoped_lock lk{cache_mutex};
+ auto& cached = cache[romfs_dir->GetFullPath()];
+ if (auto built = cached.lock()) {
+ return built;
+ }
LOG_INFO(Loader, "Using RomFS directory next to the ExeFS");
- return FileSys::CreateRomFS(romfs_dir);
+ auto built = FileSys::CreateRomFS(romfs_dir);
+ cached = built;
+ return built;
}
}
return nullptr;
@@ -233,7 +247,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
name = control->GetApplicationName();
}
- romfs = FindLooseRomFS(file_dir);
+ romfs_exefs_dir = file_dir;
if (const auto title_dir = file_dir->GetParentDirectory()) {
romfs_version = RomFSProductVersion(title_dir->GetSubdirectory("romfs"));
}
@@ -245,7 +259,17 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
, dir(std::move(directory))
, override_update(override_update_)
{
- romfs = FindLooseRomFS(dir);
+ romfs_exefs_dir = dir;
+}
+
+// Loaders made only for metadata (the game list, title lookups) never need the RomFS, and several
+// of them were alive with a whole copy each.
+const FileSys::VirtualFile& AppLoader_DeconstructedRomDirectory::LooseRomFS() {
+ if (!romfs_resolved) {
+ romfs_resolved = true;
+ romfs = FindLooseRomFS(romfs_exefs_dir);
+ }
+ return romfs;
}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) {
@@ -304,7 +328,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// Loose-ExeFS loads bypass the NCA/NSP loaders, which are the ones that normally apply
// LayeredFS to the RomFS; apply it here so mods in user/load/<titleid>/ work too.
- if (romfs != nullptr) {
+ if (LooseRomFS() != nullptr) {
const FileSys::PatchManager patch_manager(metadata.GetTitleID(),
system.GetFileSystemController(),
system.GetContentProvider());
@@ -414,7 +438,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& out_dir) {
- if (romfs == nullptr) {
+ if (LooseRomFS() == nullptr) {
return ResultStatus::ErrorNoRomFS;
}
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index b976b71ecb..db0120f4ad 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -58,8 +58,13 @@ public:
ResultStatus ReadNSOModules(Modules& out_modules) override;
private:
+ /// The loose RomFS next to the ExeFS, found (and built) on first use.
+ const FileSys::VirtualFile& LooseRomFS();
+
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
+ FileSys::VirtualDir romfs_exefs_dir; ///< Where LooseRomFS looks
+ bool romfs_resolved{};
FileSys::VirtualDir dir;
std::vector<u8> icon_data;