From d17b095b1f00a11c566985c110d56ed7a4ab402f Mon Sep 17 00:00:00 2001
From: pstef <3462925+pstef@users.noreply.github.com>
Date: Sun, 16 Aug 2026 15:19:10 +0200
Subject: [PATCH] EE/UI: put eeClampMode 4 on the clamping mode picker
Exact was reachable through the GameDB and the INI only: the desktop and
Big Picture pickers stopped at Full, and cleared fpuExactMode behind
themselves. Both now read and write it with the other three bits, so the
mode can be picked and a config already on it displays as itself.
The Big Picture choice list is sized from its own array. It had taken
the EE array's length for the VU pickers too, which held only while the
two arrays were the same length.
---
pcsx2-qt/Settings/AdvancedSettingsWidget.cpp | 25 +++++++++++++-------
pcsx2-qt/Settings/AdvancedSettingsWidget.ui | 5 ++++
pcsx2/Docs/GameIndex.md | 3 +--
pcsx2/ImGui/FullscreenUI_Settings.cpp | 24 +++++++++++--------
4 files changed, 36 insertions(+), 21 deletions(-)
diff --git a/pcsx2-qt/Settings/AdvancedSettingsWidget.cpp b/pcsx2-qt/Settings/AdvancedSettingsWidget.cpp
index 831d3a309e..d450372017 100644
--- a/pcsx2-qt/Settings/AdvancedSettingsWidget.cpp
+++ b/pcsx2-qt/Settings/AdvancedSettingsWidget.cpp
@@ -73,6 +73,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_ui.eeClampMode, tr("Clamping Mode"), tr("Normal (Default)"),
tr("Changes how ARMSX2 handles keeping floats in a standard x86 range. "
+ "Exact is Full plus the rest of the EE multiplier's one-ULP deficit, and is slower than Full. "
"The default value handles the vast majority of games; modifying this setting when a game is not having a visible problem can cause instability."));
dialog()->registerWidgetHelp(m_ui.eeRecompiler, tr("Enable Recompiler"), tr("Checked"),
@@ -103,7 +104,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_ui.eeFpuGuardedAddSub, tr("FPU Add/Sub Guard Bits"), tr("Checked"),
//: "Guard bits" = extra low mantissa bits a standards-compliant FPU keeps during add/sub; the PS2's EE FPU does not, so the recompiler masks them to match.
tr("Emulates the EE FPU's missing add/sub mantissa guard bits for hardware-accurate results. Leave checked; a few games (e.g. True Crime, Jak 3) misrender without it. "
- "Unchecking is a minor speedup for EE-FPU-heavy games verified to render correctly without it. Has no effect when the EE Clamping Mode is set to Full."));
+ "Unchecking is a minor speedup for EE-FPU-heavy games verified to render correctly without it. Has no effect when the EE Clamping Mode is set to Full or Exact."));
dialog()->registerWidgetHelp(m_ui.vu0RoundingMode, tr("VU0 Rounding Mode"), tr("Chop/Zero (Default)"), tr("Changes how ARMSX2 handles rounding while emulating the Emotion Engine's Vector Unit 0 (EE VU0). "
"The default value handles the vast majority of games; modifying this setting when a game is not having a visible problem will cause stability issues and/or crashes."));
@@ -160,6 +161,10 @@ AdvancedSettingsWidget::~AdvancedSettingsWidget() = default;
int AdvancedSettingsWidget::getGlobalClampingModeIndex(int vunum) const
{
+ // eeClampMode 4 has no VU counterpart.
+ if (vunum < 0 && Host::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", "fpuExactMode", false))
+ return 4;
+
if (Host::GetBaseBoolSettingValue(
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0SignOverflow" : "vu1SignOverflow") : "fpuFullMode"), false))
return 3;
@@ -182,6 +187,9 @@ int AdvancedSettingsWidget::getClampingModeIndex(int vunum) const
std::optional default_false = dialog()->isPerGameSettings() ? std::nullopt : std::optional(false);
std::optional default_true = dialog()->isPerGameSettings() ? std::nullopt : std::optional(true);
+ std::optional fourth;
+ if (vunum < 0)
+ fourth = dialog()->getBoolValue("EmuCore/CPU/Recompiler", "fpuExactMode", default_false);
std::optional third = dialog()->getBoolValue(
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0SignOverflow" : "vu1SignOverflow") : "fpuFullMode"), default_false);
std::optional second = dialog()->getBoolValue("EmuCore/CPU/Recompiler",
@@ -189,6 +197,8 @@ int AdvancedSettingsWidget::getClampingModeIndex(int vunum) const
std::optional first = dialog()->getBoolValue(
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0Overflow" : "vu1Overflow") : "fpuOverflow"), default_true);
+ if (fourth.has_value() && fourth.value())
+ return base + 4;
if (third.has_value() && third.value())
return base + 3;
if (second.has_value() && second.value())
@@ -203,11 +213,12 @@ int AdvancedSettingsWidget::getClampingModeIndex(int vunum) const
void AdvancedSettingsWidget::setClampingMode(int vunum, int index)
{
- std::optional first, second, third;
+ std::optional first, second, third, fourth;
if (!dialog()->isPerGameSettings() || index > 0)
{
const bool base = dialog()->isPerGameSettings() ? 1 : 0;
+ fourth = (index >= (base + 4));
third = (index >= (base + 3));
second = (index >= (base + 2));
first = (index >= (base + 1));
@@ -220,15 +231,11 @@ void AdvancedSettingsWidget::setClampingMode(int vunum, int index)
dialog()->setBoolSettingValue(
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0Overflow" : "vu1Overflow") : "fpuOverflow"), first);
- // Clear fpuExactMode (eeClampMode 4, not on this picker): ApplySanityCheck
- // rejects a config with a mode set above the one picked.
+ // fpuExactMode is eeClampMode 4's bit, and the VUs have none. It is written
+ // with the other three because ApplySanityCheck drops a config whose bits
+ // are not a whole mode to the default, not to the mode that was picked.
if (vunum < 0)
- {
- std::optional fourth;
- if (first.has_value())
- fourth = false;
dialog()->setBoolSettingValue("EmuCore/CPU/Recompiler", "fpuExactMode", fourth);
- }
}
void AdvancedSettingsWidget::onSavestateCompressionTypeChanged()
diff --git a/pcsx2-qt/Settings/AdvancedSettingsWidget.ui b/pcsx2-qt/Settings/AdvancedSettingsWidget.ui
index ce17e93239..8974f7c982 100644
--- a/pcsx2-qt/Settings/AdvancedSettingsWidget.ui
+++ b/pcsx2-qt/Settings/AdvancedSettingsWidget.ui
@@ -127,6 +127,11 @@
Full
+ -
+
+ Exact
+
+
-
diff --git a/pcsx2/Docs/GameIndex.md b/pcsx2/Docs/GameIndex.md
index cb152fc41d..db69eb2eea 100644
--- a/pcsx2/Docs/GameIndex.md
+++ b/pcsx2/Docs/GameIndex.md
@@ -122,8 +122,7 @@ The clamp modes are also numerically based.
* `2` = Clamp **Extra+Preserve Sign** (clamp results as well as operands)
* `3` = **Full Clamping** for FPU
* `4` = **Exact**: mode 3 plus the rest of the EE multiplier's one-ULP deficit.
- Multiplies it cannot decide from ft's mantissa call out of line. GameDB and
- INI only — there is no picker entry.
+ Multiplies it cannot decide from ft's mantissa call out of line.
### vuClampMode
diff --git a/pcsx2/ImGui/FullscreenUI_Settings.cpp b/pcsx2/ImGui/FullscreenUI_Settings.cpp
index 8c1101aeec..9993ceef3d 100644
--- a/pcsx2/ImGui/FullscreenUI_Settings.cpp
+++ b/pcsx2/ImGui/FullscreenUI_Settings.cpp
@@ -2678,6 +2678,10 @@ void FullscreenUI::DrawClampingModeSetting(SettingsInterface* bsi, const char* t
std::optional default_false = IsEditingGameSettings(bsi) ? std::nullopt : std::optional(false);
std::optional default_true = IsEditingGameSettings(bsi) ? std::nullopt : std::optional(true);
+ // eeClampMode 4 has no VU counterpart.
+ std::optional fourth;
+ if (vunum < 0)
+ fourth = bsi->GetOptionalBoolValue("EmuCore/CPU/Recompiler", "fpuExactMode", default_false);
std::optional third = bsi->GetOptionalBoolValue(
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0SignOverflow" : "vu1SignOverflow") : "fpuFullMode"), default_false);
std::optional second = bsi->GetOptionalBoolValue("EmuCore/CPU/Recompiler",
@@ -2686,7 +2690,9 @@ void FullscreenUI::DrawClampingModeSetting(SettingsInterface* bsi, const char* t
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0Overflow" : "vu1Overflow") : "fpuOverflow"), default_true);
int index;
- if (third.has_value() && third.value())
+ if (fourth.has_value() && fourth.value())
+ index = base + 4;
+ else if (third.has_value() && third.value())
index = base + 3;
else if (second.has_value() && second.value())
index = base + 2;
@@ -2703,6 +2709,7 @@ void FullscreenUI::DrawClampingModeSetting(SettingsInterface* bsi, const char* t
FSUI_NSTR("Normal (Default)"),
FSUI_NSTR("Extra + Preserve Sign"),
FSUI_NSTR("Full"),
+ FSUI_NSTR("Exact"),
};
static constexpr const char* vu_clamping_mode_settings[] = {
FSUI_NSTR("Use Global Setting"),
@@ -2712,13 +2719,14 @@ void FullscreenUI::DrawClampingModeSetting(SettingsInterface* bsi, const char* t
FSUI_NSTR("Extra + Preserve Sign"),
};
const char* const* options = (vunum >= 0) ? vu_clamping_mode_settings : ee_clamping_mode_settings;
+ const int option_count = static_cast((vunum >= 0) ? std::size(vu_clamping_mode_settings) : std::size(ee_clamping_mode_settings));
const int setting_offset = IsEditingGameSettings(bsi) ? 0 : 1;
if (MenuButtonWithValue(title, summary, Host::TranslateToCString(TR_CONTEXT, options[index + setting_offset])))
{
ImGuiFullscreen::ChoiceDialogOptions cd_options;
- cd_options.reserve(std::size(ee_clamping_mode_settings));
- for (int i = setting_offset; i < static_cast(std::size(ee_clamping_mode_settings)); i++)
+ cd_options.reserve(option_count);
+ for (int i = setting_offset; i < option_count; i++)
cd_options.emplace_back(Host::TranslateToString(TR_CONTEXT, options[i]), (i == (index + setting_offset)));
OpenChoiceDialog(title, false, std::move(cd_options),
[game_settings = IsEditingGameSettings(bsi), vunum](s32 index, const std::string& title, bool checked) {
@@ -2726,11 +2734,12 @@ void FullscreenUI::DrawClampingModeSetting(SettingsInterface* bsi, const char* t
{
auto lock = Host::GetSettingsLock();
- std::optional first, second, third;
+ std::optional first, second, third, fourth;
if (!game_settings || index > 0)
{
const bool base = game_settings ? 1 : 0;
+ fourth = (index >= (base + 4));
third = (index >= (base + 3));
second = (index >= (base + 2));
first = (index >= (base + 1));
@@ -2745,12 +2754,7 @@ void FullscreenUI::DrawClampingModeSetting(SettingsInterface* bsi, const char* t
"EmuCore/CPU/Recompiler", (vunum >= 0 ? ((vunum == 0) ? "vu0Overflow" : "vu1Overflow") : "fpuOverflow"), first);
// Same write as AdvancedSettingsWidget::setClampingMode.
if (vunum < 0)
- {
- std::optional fourth;
- if (first.has_value())
- fourth = false;
bsi->SetOptionalBoolValue("EmuCore/CPU/Recompiler", "fpuExactMode", fourth);
- }
SetSettingsChanged(bsi);
}
@@ -6459,6 +6463,7 @@ TRANSLATE_NOOP("FullscreenUI", "2 Frames");
TRANSLATE_NOOP("FullscreenUI", "3 Frames");
TRANSLATE_NOOP("FullscreenUI", "Extra + Preserve Sign");
TRANSLATE_NOOP("FullscreenUI", "Full");
+TRANSLATE_NOOP("FullscreenUI", "Exact");
TRANSLATE_NOOP("FullscreenUI", "Extra");
TRANSLATE_NOOP("FullscreenUI", "Automatic (Default)");
TRANSLATE_NOOP("FullscreenUI", "Direct3D 11 (Legacy)");
@@ -6951,7 +6956,6 @@ TRANSLATE_NOOP("FullscreenUI", "Enable PINE");
TRANSLATE_NOOP("FullscreenUI", "PINE Slot");
TRANSLATE_NOOP("FullscreenUI", "Show Cheats For All CRCs");
TRANSLATE_NOOP("FullscreenUI", "Show Patches For All CRCs");
-TRANSLATE_NOOP("FullscreenUI", "FPU Multiply Hack");
TRANSLATE_NOOP("FullscreenUI", "Use Software Renderer For FMVs");
TRANSLATE_NOOP("FullscreenUI", "Skip MPEG Hack");
TRANSLATE_NOOP("FullscreenUI", "Preload TLB Hack");