diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-08 17:33:52 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-05-08 17:33:52 -0500 |
| commit | 268c9ad92f9700a855d479d91a2e47ff381216f3 (patch) | |
| tree | b0cb8dcdd65a0fc06eb570a5bc9b802222f0a589 | |
| parent | cf04c1780601dd3a4fc0e7ae079dc2aa5dbc13f2 (diff) | |
vfs: per-executable opt-in toggle for PE bridge
Adds Executable::UseVfsBridge flag (off by default) and a "Use VFS
bridge (experimental)" checkbox in the executables editor next to
"Open in terminal" / "Use Proton". The flag plumbs through to
SpawnParameters::useVfsBridge and gates the FLUORINE_VFS_INDEX/DATA_DIR/
MOUNT env vars in spawn().
Without those env vars, the staged fluorine_vfs.dll still AppInit-loads
into every PE process in the prefix, but its hook_worker bails at
load_index() and installs zero hooks — equivalent to the bridge being
disabled. No separate gating is needed in protonlauncher.cpp because
its hid.dll-proxy staging and prewarm are already conditioned on
vfsBridgeIndexPresent(envVars).
Existing instances load with useVfsBridge=false (key absent from
ModOrganizer.ini; the loader treats absence as false). New executables
created via the plugin path also default off. Users who want the perf
win for a particular game tick the box; everyone else launches via the
correct, slower Wine + FUSE path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| -rw-r--r-- | src/src/editexecutablesdialog.cpp | 13 | ||||
| -rw-r--r-- | src/src/editexecutablesdialog.ui | 13 | ||||
| -rw-r--r-- | src/src/executableslist.cpp | 12 | ||||
| -rw-r--r-- | src/src/executableslist.h | 4 | ||||
| -rw-r--r-- | src/src/processrunner.cpp | 5 | ||||
| -rw-r--r-- | src/src/spawn.cpp | 22 | ||||
| -rw-r--r-- | src/src/spawn.h | 13 |
7 files changed, 65 insertions, 17 deletions
diff --git a/src/src/editexecutablesdialog.cpp b/src/src/editexecutablesdialog.cpp index 723b3f5..4b8a478 100644 --- a/src/src/editexecutablesdialog.cpp +++ b/src/src/editexecutablesdialog.cpp @@ -129,6 +129,9 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, int sel, connect(ui->useTerminal, &QCheckBox::toggled, [&] {
save();
});
+ connect(ui->useVfsBridge, &QCheckBox::toggled, [&] {
+ save();
+ });
connect(ui->list->model(), &QAbstractItemModel::rowsMoved, [&] {
saveOrder();
});
@@ -398,6 +401,8 @@ void EditExecutablesDialog::clearEdits() ui->useProton->setChecked(true);
ui->useTerminal->setEnabled(false);
ui->useTerminal->setChecked(false);
+ ui->useVfsBridge->setEnabled(false);
+ ui->useVfsBridge->setChecked(false);
m_lastGoodTitle = "";
}
@@ -416,6 +421,7 @@ void EditExecutablesDialog::setEdits(const Executable& e) ui->hide->setChecked(e.hide());
ui->useProton->setChecked(e.useProton());
ui->useTerminal->setChecked(e.useTerminal());
+ ui->useVfsBridge->setChecked(e.useVfsBridge());
m_lastGoodTitle = e.title();
@@ -464,6 +470,7 @@ void EditExecutablesDialog::setEdits(const Executable& e) ui->hide->setEnabled(true);
ui->useProton->setEnabled(true);
ui->useTerminal->setEnabled(true);
+ ui->useVfsBridge->setEnabled(true);
}
void EditExecutablesDialog::save()
@@ -547,6 +554,12 @@ void EditExecutablesDialog::save() e->flags(e->flags() & (~Executable::UseTerminal));
}
+ if (ui->useVfsBridge->isChecked()) {
+ e->flags(e->flags() | Executable::UseVfsBridge);
+ } else {
+ e->flags(e->flags() & (~Executable::UseVfsBridge));
+ }
+
setDirty(true);
}
diff --git a/src/src/editexecutablesdialog.ui b/src/src/editexecutablesdialog.ui index 8ac01f6..f323558 100644 --- a/src/src/editexecutablesdialog.ui +++ b/src/src/editexecutablesdialog.ui @@ -488,6 +488,19 @@ Right now the only case I know of where this needs to be overwritten is for the </widget>
</item>
<item>
+ <widget class="QCheckBox" name="useVfsBridge">
+ <property name="toolTip">
+ <string>Inject the PE-side VFS bridge (fluorine_vfs.dll) so file lookups in the game's data dir hit a static index instead of going through Wine + FUSE every time. Saves several seconds on first boot for large modlists. Off by default — some modlists crash with the bridge enabled until indexer/negcache work is finished.</string>
+ </property>
+ <property name="text">
+ <string>Use VFS bridge (experimental)</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>(*) Profile specific</string>
diff --git a/src/src/executableslist.cpp b/src/src/executableslist.cpp index ca4f295..49b9e86 100644 --- a/src/src/executableslist.cpp +++ b/src/src/executableslist.cpp @@ -96,6 +96,12 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, const Settings& s) if (map["useTerminal"].toBool()) flags |= Executable::UseTerminal; + // Default to UseVfsBridge=false. The PE-side bridge is opt-in + // because some modlists (notably ones with NPCWaterAIFix) crash + // with it enabled until the indexer/negcache work is finished. + if (map.contains("useVfsBridge") && map["useVfsBridge"].toBool()) + flags |= Executable::UseVfsBridge; + if (map.contains("custom")) { // the "custom" setting only exists in older versions needsUpgrade = true; @@ -136,6 +142,7 @@ void ExecutablesList::store(Settings& s) map["minimizeToSystemTray"] = item.minimizeToSystemTray(); map["useProton"] = item.useProton(); map["useTerminal"] = item.useTerminal(); + map["useVfsBridge"] = item.useVfsBridge(); v.push_back(std::move(map)); } @@ -511,6 +518,11 @@ bool Executable::useTerminal() const return m_flags.testFlag(UseTerminal); } +bool Executable::useVfsBridge() const +{ + return m_flags.testFlag(UseVfsBridge); +} + void Executable::mergeFrom(const Executable& other) { // this happens after executables are loaded from settings and plugin diff --git a/src/src/executableslist.h b/src/src/executableslist.h index 8ec8cbb..86a5476 100644 --- a/src/src/executableslist.h +++ b/src/src/executableslist.h @@ -50,7 +50,8 @@ public: Hide = 0x08,
MinimizeToSystemTray = 0x16,
UseProton = 0x20,
- UseTerminal = 0x40
+ UseTerminal = 0x40,
+ UseVfsBridge = 0x80
};
Q_DECLARE_FLAGS(Flags, Flag);
@@ -83,6 +84,7 @@ public: bool hide() const override;
bool useProton() const;
bool useTerminal() const;
+ bool useVfsBridge() const;
void mergeFrom(const Executable& other);
diff --git a/src/src/processrunner.cpp b/src/src/processrunner.cpp index 4fb8a2c..ed081e2 100644 --- a/src/src/processrunner.cpp +++ b/src/src/processrunner.cpp @@ -911,8 +911,9 @@ ProcessRunner& ProcessRunner::setFromExecutable(const Executable& exe) setCustomOverwrite(customOverwrite); setForcedLibraries(forcedLibraries); - m_sp.useProton = exe.useProton(); - m_sp.useTerminal = exe.useTerminal(); + m_sp.useProton = exe.useProton(); + m_sp.useTerminal = exe.useTerminal(); + m_sp.useVfsBridge = exe.useVfsBridge(); return *this; } diff --git a/src/src/spawn.cpp b/src/src/spawn.cpp index 5ed3db1..f7a4248 100644 --- a/src/src/spawn.cpp +++ b/src/src/spawn.cpp @@ -476,14 +476,20 @@ int spawn(const SpawnParameters& sp, pid_t& processId) MOBase::log::info("Proton disabled for this executable, launching directly"); } - if (!sp.vfsBridgeIndexPath.isEmpty()) { - launcher.addEnvVar("FLUORINE_VFS_INDEX", sp.vfsBridgeIndexPath); - } - if (!sp.vfsBridgeDataDir.isEmpty()) { - launcher.addEnvVar("FLUORINE_VFS_DATA_DIR", sp.vfsBridgeDataDir); - } - if (!sp.vfsBridgeMountPoint.isEmpty()) { - launcher.addEnvVar("FLUORINE_VFS_MOUNT", sp.vfsBridgeMountPoint); + // PE-side VFS bridge is per-executable opt-in. Without these env + // vars the staged fluorine_vfs.dll loads but its hook_worker bails + // out at load_index() (FLUORINE_VFS_INDEX unset) and installs zero + // hooks — equivalent to the bridge being disabled. + if (sp.useVfsBridge) { + if (!sp.vfsBridgeIndexPath.isEmpty()) { + launcher.addEnvVar("FLUORINE_VFS_INDEX", sp.vfsBridgeIndexPath); + } + if (!sp.vfsBridgeDataDir.isEmpty()) { + launcher.addEnvVar("FLUORINE_VFS_DATA_DIR", sp.vfsBridgeDataDir); + } + if (!sp.vfsBridgeMountPoint.isEmpty()) { + launcher.addEnvVar("FLUORINE_VFS_MOUNT", sp.vfsBridgeMountPoint); + } } launcher.setUseTerminal(sp.useTerminal); diff --git a/src/src/spawn.h b/src/src/spawn.h index 77ad204..010edb9 100644 --- a/src/src/spawn.h +++ b/src/src/spawn.h @@ -53,6 +53,7 @@ struct SpawnParameters bool hooked = false;
bool useProton = true;
bool useTerminal = false;
+ bool useVfsBridge = false;
int stdOut = -1;
int stdErr = -1;
// When both are set and unprivileged user namespaces are available,
@@ -60,12 +61,12 @@ struct SpawnParameters // of `saveBindMountSource` for the duration of the game process tree.
// Used to redirect `<prefix>/__MO_Saves` to the profile's saves dir
// without symlinks, which Wine can accidentally replace.
- QString saveBindMountSource; - QString saveBindMountTarget; - QString vfsBridgeIndexPath; - QString vfsBridgeDataDir; - QString vfsBridgeMountPoint; -}; + QString saveBindMountSource;
+ QString saveBindMountTarget;
+ QString vfsBridgeIndexPath;
+ QString vfsBridgeDataDir;
+ QString vfsBridgeMountPoint;
+};
bool checkSteam(QWidget* parent, const SpawnParameters& sp, const QDir& gameDirectory,
const QString& steamAppID, const Settings& settings);
|
