VMManager: Refactor Affinity Control to Thread Pinning

Instead of having control over specific threads, thread pinning puts the
EE/VU/GS threads on the most performant cores, then the software threads
on the remaining cores, but only if they're in the same cluster.

This way we don't end up pinning across clusters with different
performance characteristics, which would harm instead of help software
renderer performance.

Also unpins on shutdown, that way we don't keep CPU cores awake.
This commit is contained in:
Stenzek 2024-06-15 23:34:50 +10:00 committed by Connor McLaughlin
parent b1f051df40
commit 8766d0b676
11 changed files with 184 additions and 253 deletions

View File

@ -29,6 +29,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsWindow* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vu0Recompiler, "EmuCore/CPU/Recompiler", "EnableVU0", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vu1Recompiler, "EmuCore/CPU/Recompiler", "EnableVU1", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vuFlagHack, "EmuCore/Speedhacks", "vuFlagHack", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeRoundingMode, "EmuCore/CPU", "FPU.Roundmode", static_cast<int>(FPRoundMode::ChopZero));
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeDivRoundingMode, "EmuCore/CPU", "FPUDiv.Roundmode", static_cast<int>(FPRoundMode::Nearest));
@ -101,6 +102,9 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsWindow* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.vu1ClampMode, tr("VU1 Clamping Mode"), tr("Normal (Default)"), tr("Changes how PCSX2 handles keeping floats in a standard x86 range in the Emotion Engine's Vector Unit 1 (EE VU1). "
"The default value handles the vast majority of games; <b>modifying this setting when a game is not having a visible problem can cause instability.</b>"));
dialog->registerWidgetHelp(m_ui.instantVU1, tr("Enable Instant VU1"), tr("Checked"), tr("Runs VU1 instantly. Provides a modest speed improvement in most games. "
"Safe for most games, but a few games may exhibit graphical errors."));
//: VU0 = Vector Unit 0. One of the PS2's processors.
dialog->registerWidgetHelp(m_ui.vu0Recompiler, tr("Enable VU0 Recompiler (Micro Mode)"), tr("Checked"), tr("Enables VU0 Recompiler."));

View File

@ -33,8 +33,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>807</width>
<height>723</height>
<width>790</width>
<height>765</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -202,7 +202,7 @@
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="extraMemory">
<property name="text">
<property name="text">
<string>Enable 128MB RAM (Dev Console)</string>
</property>
</widget>
@ -272,6 +272,13 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="instantVU1">
<property name="text">
<string>Enable Instant VU1</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
@ -494,7 +501,7 @@
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View File

@ -40,10 +40,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
m_ui.optimalFramePacing->setTristate(dialog->isPerGameSettings());
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeCycleSkipping, "EmuCore/Speedhacks", "EECycleSkip", DEFAULT_EE_CYCLE_SKIP);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.affinityControl, "EmuCore/CPU", "AffinityControlMode", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.threadPinning, "EmuCore", "EnableThreadPinning", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.precacheCDVD, "EmuCore", "CdvdPrecache", false);
@ -113,16 +112,13 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
tr("Makes the emulated Emotion Engine skip cycles. "
//: SOTC = Shadow of the Colossus. A game's title, should not be translated unless an official translation exists.
"Helps a small subset of games like SOTC. Most of the time it's harmful to performance."));
dialog->registerWidgetHelp(m_ui.affinityControl, tr("Affinity Control"), tr("Disabled"),
dialog->registerWidgetHelp(m_ui.threadPinning, tr("Enable Thread Pinning"), tr("Unchecked"),
tr("Sets the priority for specific threads in a specific order ignoring the system scheduler. "
//: P-Core = Performance Core, E-Core = Efficiency Core. See if Intel has official translations for these terms.
"May help CPUs with big (P) and little (E) cores (e.g. Intel 12th or newer generation CPUs from Intel or other vendors such as AMD)."));
dialog->registerWidgetHelp(m_ui.MTVU, tr("Enable Multithreaded VU1 (MTVU1)"), tr("Checked"),
tr("Generally a speedup on CPUs with 4 or more cores. "
"Safe for most games, but a few are incompatible and may hang."));
dialog->registerWidgetHelp(m_ui.instantVU1, tr("Enable Instant VU1"), tr("Checked"),
tr("Runs VU1 instantly. Provides a modest speed improvement in most games. "
"Safe for most games, but a few games may exhibit graphical errors."));
dialog->registerWidgetHelp(m_ui.fastCDVD, tr("Enable Fast CDVD"), tr("Unchecked"),
tr("Fast disc access, less loading times. Check HDLoader compatibility lists for known games that have issues with this."));
dialog->registerWidgetHelp(m_ui.precacheCDVD, tr("Enable CDVD Precaching"), tr("Unchecked"),

View File

@ -68,90 +68,6 @@
<string>System Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="3" column="0" colspan="2">
<layout class="QGridLayout" name="systemSettingsLayout">
<item row="0" column="1">
<widget class="QCheckBox" name="instantVU1">
<property name="text">
<string>Enable Instant VU1</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cheats">
<property name="text">
<string>Enable Cheats</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="MTVU">
<property name="text">
<string>Enable Multithreaded VU1 (MTVU)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="hostFilesystem">
<property name="text">
<string>Enable Host Filesystem</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="fastCDVD">
<property name="text">
<string>Enable Fast CDVD</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="precacheCDVD">
<property name="text">
<string>Enable CDVD Precaching</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>EE Cycle Skipping:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="eeCycleSkipping">
<item>
<property name="text">
<string>Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Mild Underclock</string>
</property>
</item>
<item>
<property name="text">
<string>Moderate Underclock</string>
</property>
</item>
<item>
<property name="text">
<string>Maximum Underclock</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>EE Cycle Rate:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="eeCycleRate">
<item>
@ -191,15 +107,8 @@
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Affinity Control:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="affinityControl">
<item row="1" column="1">
<widget class="QComboBox" name="eeCycleSkipping">
<item>
<property name="text">
<string>Disabled</string>
@ -207,34 +116,79 @@
</item>
<item>
<property name="text">
<string>EE &gt; VU &gt; GS</string>
<string>Mild Underclock</string>
</property>
</item>
<item>
<property name="text">
<string>EE &gt; GS &gt; VU</string>
<string>Moderate Underclock</string>
</property>
</item>
<item>
<property name="text">
<string>VU &gt; EE &gt; GS</string>
<string>Maximum Underclock</string>
</property>
</item>
<item>
<property name="text">
<string>VU &gt; GS &gt; EE</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QGridLayout" name="systemSettingsLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="cheats">
<property name="text">
<string>Enable Cheats</string>
</property>
</widget>
</item>
<item>
<property name="text">
<string>GS &gt; EE &gt; VU</string>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="MTVU">
<property name="text">
<string>Enable Multithreaded VU1 (MTVU)</string>
</property>
</widget>
</item>
<item>
<property name="text">
<string>GS &gt; VU &gt; EE</string>
</property>
<item row="1" column="1">
<widget class="QCheckBox" name="hostFilesystem">
<property name="text">
<string>Enable Host Filesystem</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="fastCDVD">
<property name="text">
<string>Enable Fast CDVD</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="precacheCDVD">
<property name="text">
<string>Enable CDVD Precaching</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="threadPinning">
<property name="text">
<string>Enable Thread Pinning</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>EE Cycle Rate:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>EE Cycle Skipping:</string>
</property>
</widget>
</item>
</layout>

View File

@ -545,8 +545,6 @@ struct Pcsx2Config
FPControlRegister VU0FPCR;
FPControlRegister VU1FPCR;
u32 AffinityControlMode;
CpuOptions();
void LoadSave(SettingsWrapper& wrap);
void ApplySanityCheck();
@ -1119,6 +1117,7 @@ struct Pcsx2Config
EnableNoInterlacingPatches : 1,
EnableFastBoot : 1,
EnableFastBootFastForward : 1,
EnableThreadPinning : 1,
// TODO - Vaser - where are these settings exposed in the Qt UI?
EnableRecordingTools : 1,
EnableGameFixes : 1, // enables automatic game fixes

View File

@ -1209,23 +1209,15 @@ GSRasterizerList::~GSRasterizerList()
_aligned_free(m_scanline);
}
void GSRasterizerList::OnWorkerStartup(int i)
void GSRasterizerList::OnWorkerStartup(int i, u64 affinity)
{
Threading::SetNameOfCurrentThread(StringUtil::StdStringFromFormat("GS-SW-%d", i).c_str());
Threading::ThreadHandle handle(Threading::ThreadHandle::GetForCallingThread());
if (EmuConfig.Cpu.AffinityControlMode != 0)
if (affinity != 0)
{
const std::vector<u32>& procs = VMManager::GetSortedProcessorList();
const u32 processor_index = (THREAD_VU1 ? 3 : 2) + i;
if (processor_index < procs.size())
{
const u32 procid = procs[processor_index];
const u64 affinity = static_cast<u64>(1) << procid;
Console.WriteLn("Pinning GS thread %d to CPU %u (0x%llx)", i, procid, affinity);
handle.SetAffinity(affinity);
}
INFO_LOG("Pinning GS thread {} to CPU {} (0x{:x})", i, std::countr_zero(affinity), affinity);
handle.SetAffinity(affinity);
}
PerformanceMetrics::SetGSSWThread(i, std::move(handle));
@ -1306,11 +1298,18 @@ std::unique_ptr<IRasterizer> GSRasterizerList::Create(int threads)
std::unique_ptr<GSRasterizerList> rl(new GSRasterizerList(threads));
const std::vector<u32>& procs = VMManager::Internal::GetSoftwareRendererProcessorList();
const bool pin = (EmuConfig.EnableThreadPinning && static_cast<size_t>(threads) <= procs.size());
if (EmuConfig.EnableThreadPinning && !pin)
WARNING_LOG("Not pinning SW threads, we need {} processors, but only have {}", threads, procs.size());
for (int i = 0; i < threads; i++)
{
const u64 affinity = pin ? (static_cast<u64>(1u) << procs[i]) : 0;
rl->m_r.push_back(std::unique_ptr<GSRasterizer>(new GSRasterizer(&rl->m_ds, i, threads)));
auto& r = *rl->m_r[i];
rl->m_workers.push_back(std::unique_ptr<GSWorker>(new GSWorker([i]() { GSRasterizerList::OnWorkerStartup(i); },
rl->m_workers.push_back(std::unique_ptr<GSWorker>(new GSWorker(
[i, affinity]() { GSRasterizerList::OnWorkerStartup(i, affinity); },
[&r](GSRingHeap::SharedPtr<GSRasterizerData>& item) { r.Draw(*item.get()); },
[i]() { GSRasterizerList::OnWorkerShutdown(i); })));
}

View File

@ -166,7 +166,7 @@ protected:
GSRasterizerList(int threads);
static void OnWorkerStartup(int i);
static void OnWorkerStartup(int i, u64 affinity);
static void OnWorkerShutdown(int i);
public:

View File

@ -3275,15 +3275,6 @@ void FullscreenUI::DrawEmulationSettingsPage()
FSUI_NSTR("Moderate Underclock"),
FSUI_NSTR("Maximum Underclock"),
};
static constexpr const char* affinity_control_settings[] = {
FSUI_NSTR("Disabled"),
FSUI_NSTR("EE > VU > GS"),
FSUI_NSTR("EE > GS > VU"),
FSUI_NSTR("VU > EE > GS"),
FSUI_NSTR("VU > GS > EE"),
FSUI_NSTR("GS > EE > VU"),
FSUI_NSTR("GS > VU > EE"),
};
static constexpr const char* queue_entries[] = {
FSUI_NSTR("0 Frames (Hard Sync)"),
FSUI_NSTR("1 Frame"),
@ -3311,14 +3302,11 @@ void FullscreenUI::DrawEmulationSettingsPage()
DrawIntListSetting(bsi, FSUI_CSTR("EE Cycle Skipping"),
FSUI_CSTR("Makes the emulated Emotion Engine skip cycles. Helps a small subset of games like SOTC. Most of the time it's harmful to performance."), "EmuCore/Speedhacks", "EECycleSkip", 0,
ee_cycle_skip_settings, std::size(ee_cycle_skip_settings), true);
DrawIntListSetting(bsi, FSUI_CSTR("Affinity Control Mode"),
FSUI_CSTR("Pins emulation threads to CPU cores to potentially improve performance/frame time variance."), "EmuCore/CPU",
"AffinityControlMode", 0, affinity_control_settings, std::size(affinity_control_settings), true);
DrawToggleSetting(bsi, FSUI_CSTR("Enable MTVU (Multi-Threaded VU1)"),
FSUI_CSTR("Generally a speedup on CPUs with 4 or more cores. Safe for most games, but a few are incompatible and may hang."), "EmuCore/Speedhacks", "vuThread", false);
DrawToggleSetting(bsi, FSUI_CSTR("Enable Instant VU1"),
FSUI_CSTR("Runs VU1 instantly. Provides a modest speed improvement in most games. Safe for most games, but a few games may exhibit graphical errors."),
"EmuCore/Speedhacks", "vu1Instant", true);
DrawToggleSetting(bsi, FSUI_CSTR("Thread Pinning"),
FSUI_CSTR("Pins emulation threads to CPU cores to potentially improve performance/frame time variance."), "EmuCore",
"EnableThreadPinning", false);
DrawToggleSetting(
bsi, FSUI_CSTR("Enable Cheats"), FSUI_CSTR("Enables loading cheats from pnach files."), "EmuCore", "EnableCheats", false);
DrawToggleSetting(bsi, FSUI_CSTR("Enable Host Filesystem"),
@ -4769,6 +4757,9 @@ void FullscreenUI::DrawAdvancedSettingsPage()
true);
DrawToggleSetting(bsi, FSUI_CSTR("Enable VU Flag Optimization"),
FSUI_CSTR("Good speedup and high compatibility, may cause graphical errors."), "EmuCore/Speedhacks", "vuFlagHack", true);
DrawToggleSetting(bsi, FSUI_CSTR("Enable Instant VU1"),
FSUI_CSTR("Runs VU1 instantly. Provides a modest speed improvement in most games. Safe for most games, but a few games may exhibit graphical errors."),
"EmuCore/Speedhacks", "vu1Instant", true);
MenuHeading(FSUI_CSTR("I/O Processor"));
DrawToggleSetting(bsi, FSUI_CSTR("Enable IOP Recompiler"),
@ -6944,12 +6935,10 @@ TRANSLATE_NOOP("FullscreenUI", "EE Cycle Rate");
TRANSLATE_NOOP("FullscreenUI", "Underclocks or overclocks the emulated Emotion Engine CPU.");
TRANSLATE_NOOP("FullscreenUI", "EE Cycle Skipping");
TRANSLATE_NOOP("FullscreenUI", "Makes the emulated Emotion Engine skip cycles. Helps a small subset of games like SOTC. Most of the time it's harmful to performance.");
TRANSLATE_NOOP("FullscreenUI", "Affinity Control Mode");
TRANSLATE_NOOP("FullscreenUI", "Pins emulation threads to CPU cores to potentially improve performance/frame time variance.");
TRANSLATE_NOOP("FullscreenUI", "Enable MTVU (Multi-Threaded VU1)");
TRANSLATE_NOOP("FullscreenUI", "Generally a speedup on CPUs with 4 or more cores. Safe for most games, but a few are incompatible and may hang.");
TRANSLATE_NOOP("FullscreenUI", "Enable Instant VU1");
TRANSLATE_NOOP("FullscreenUI", "Runs VU1 instantly. Provides a modest speed improvement in most games. Safe for most games, but a few games may exhibit graphical errors.");
TRANSLATE_NOOP("FullscreenUI", "Thread Pinning");
TRANSLATE_NOOP("FullscreenUI", "Pins emulation threads to CPU cores to potentially improve performance/frame time variance.");
TRANSLATE_NOOP("FullscreenUI", "Enable Cheats");
TRANSLATE_NOOP("FullscreenUI", "Enables loading cheats from pnach files.");
TRANSLATE_NOOP("FullscreenUI", "Enable Host Filesystem");
@ -7224,6 +7213,8 @@ TRANSLATE_NOOP("FullscreenUI", "New Vector Unit recompiler with much improved co
TRANSLATE_NOOP("FullscreenUI", "Enable VU1 Recompiler");
TRANSLATE_NOOP("FullscreenUI", "Enable VU Flag Optimization");
TRANSLATE_NOOP("FullscreenUI", "Good speedup and high compatibility, may cause graphical errors.");
TRANSLATE_NOOP("FullscreenUI", "Enable Instant VU1");
TRANSLATE_NOOP("FullscreenUI", "Runs VU1 instantly. Provides a modest speed improvement in most games. Safe for most games, but a few games may exhibit graphical errors.");
TRANSLATE_NOOP("FullscreenUI", "I/O Processor");
TRANSLATE_NOOP("FullscreenUI", "Enable IOP Recompiler");
TRANSLATE_NOOP("FullscreenUI", "Performs just-in-time binary translation of 32-bit MIPS-I machine code to native code.");
@ -7391,13 +7382,6 @@ TRANSLATE_NOOP("FullscreenUI", "Normal (Default)");
TRANSLATE_NOOP("FullscreenUI", "Mild Underclock");
TRANSLATE_NOOP("FullscreenUI", "Moderate Underclock");
TRANSLATE_NOOP("FullscreenUI", "Maximum Underclock");
TRANSLATE_NOOP("FullscreenUI", "Disabled");
TRANSLATE_NOOP("FullscreenUI", "EE > VU > GS");
TRANSLATE_NOOP("FullscreenUI", "EE > GS > VU");
TRANSLATE_NOOP("FullscreenUI", "VU > EE > GS");
TRANSLATE_NOOP("FullscreenUI", "VU > GS > EE");
TRANSLATE_NOOP("FullscreenUI", "GS > EE > VU");
TRANSLATE_NOOP("FullscreenUI", "GS > VU > EE");
TRANSLATE_NOOP("FullscreenUI", "0 Frames (Hard Sync)");
TRANSLATE_NOOP("FullscreenUI", "1 Frame");
TRANSLATE_NOOP("FullscreenUI", "2 Frames");
@ -7491,6 +7475,7 @@ TRANSLATE_NOOP("FullscreenUI", "Sprites/Triangles");
TRANSLATE_NOOP("FullscreenUI", "Blended Sprites/Triangles");
TRANSLATE_NOOP("FullscreenUI", "1 (Normal)");
TRANSLATE_NOOP("FullscreenUI", "2 (Aggressive)");
TRANSLATE_NOOP("FullscreenUI", "Disabled");
TRANSLATE_NOOP("FullscreenUI", "Inside Target");
TRANSLATE_NOOP("FullscreenUI", "Merge Targets");
TRANSLATE_NOOP("FullscreenUI", "Normal (Vertex)");

View File

@ -499,7 +499,7 @@ bool Pcsx2Config::CpuOptions::operator!=(const CpuOptions& right) const
bool Pcsx2Config::CpuOptions::operator==(const CpuOptions& right) const
{
return OpEqu(FPUFPCR) && OpEqu(FPUDivFPCR) && OpEqu(VU0FPCR) && OpEqu(VU1FPCR) && OpEqu(AffinityControlMode) && OpEqu(Recompiler);
return OpEqu(FPUFPCR) && OpEqu(FPUDivFPCR) && OpEqu(VU0FPCR) && OpEqu(VU1FPCR) && OpEqu(Recompiler);
}
Pcsx2Config::CpuOptions::CpuOptions()
@ -512,14 +512,11 @@ Pcsx2Config::CpuOptions::CpuOptions()
VU0FPCR = DEFAULT_VU_FP_CONTROL_REGISTER;
VU1FPCR = DEFAULT_VU_FP_CONTROL_REGISTER;
AffinityControlMode = 0;
ExtraMemory = false;
}
void Pcsx2Config::CpuOptions::ApplySanityCheck()
{
AffinityControlMode = std::min<u32>(AffinityControlMode, 6);
Recompiler.ApplySanityCheck();
}
@ -544,7 +541,6 @@ void Pcsx2Config::CpuOptions::LoadSave(SettingsWrapper& wrap)
read_fpcr(VU0FPCR, "VU0");
read_fpcr(VU1FPCR, "VU1");
SettingsWrapEntry(AffinityControlMode);
SettingsWrapBitBool(ExtraMemory);
Recompiler.LoadSave(wrap);
@ -1714,6 +1710,7 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
SettingsWrapBitBool(EnableNoInterlacingPatches);
SettingsWrapBitBool(EnableFastBoot);
SettingsWrapBitBool(EnableFastBootFastForward);
SettingsWrapBitBool(EnableThreadPinning);
SettingsWrapBitBool(EnableRecordingTools);
SettingsWrapBitBool(EnableGameFixes);
SettingsWrapBitBool(SaveStateOnShutdown);

View File

@ -178,6 +178,7 @@ static std::string s_input_profile_name;
static u32 s_frame_advance_count = 0;
static bool s_fast_boot_requested = false;
static bool s_gs_open_on_initialize = false;
static bool s_thread_affinities_set = false;
static LimiterModeType s_limiter_mode = LimiterModeType::Nominal;
static s64 s_limiter_ticks_per_frame = 0;
@ -1659,6 +1660,7 @@ void VMManager::Shutdown(bool save_resume_state)
FullscreenUI::OnVMDestroyed();
SaveStateSelectorUI::Clear();
UpdateInhibitScreensaver(false);
SetEmuThreadAffinities();
Host::OnVMDestroyed();
// clear out any potentially-incorrect settings from the last game
@ -2836,12 +2838,6 @@ void VMManager::CheckForCPUConfigChanges(const Pcsx2Config& old_config)
// possible and reset next time we're called.
s_cpu_implementation_changed = true;
}
if (EmuConfig.Cpu.AffinityControlMode != old_config.Cpu.AffinityControlMode ||
EmuConfig.Speedhacks.vuThread != old_config.Speedhacks.vuThread)
{
SetEmuThreadAffinities();
}
}
void VMManager::CheckForGSConfigChanges(const Pcsx2Config& old_config)
@ -2972,6 +2968,12 @@ void VMManager::CheckForMiscConfigChanges(const Pcsx2Config& old_config)
else
ShutdownDiscordPresence();
}
if (HasValidVM() && (EmuConfig.EnableThreadPinning != old_config.EnableThreadPinning ||
(s_thread_affinities_set && EmuConfig.Speedhacks.vuThread != old_config.Speedhacks.vuThread)))
{
SetEmuThreadAffinities();
}
}
void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config)
@ -3333,6 +3335,7 @@ void VMManager::SetTimerResolutionIncreased(bool enabled)
#endif
static std::vector<u32> s_processor_list;
static std::vector<u32> s_software_renderer_processor_list;
static std::once_flag s_processor_list_initialized;
#if defined(__linux__) || defined(_WIN32)
@ -3352,61 +3355,44 @@ static void InitializeProcessorList()
{
if (!cpuinfo_initialize())
{
Console.Error("cpuinfo_initialize() failed");
ERROR_LOG("cpuinfo_initialize() failed");
return;
}
const u32 cluster_count = cpuinfo_get_clusters_count();
if (cluster_count == 0)
INFO_LOG("Processor count: {} cores, {} processors, {} clusters",
cpuinfo_get_cores_count(), cpuinfo_get_processors_count(), cpuinfo_get_clusters_count());
const u32 processor_count = cpuinfo_get_processors_count();
std::vector<const cpuinfo_processor*> processors;
for (u32 i = 0; i < processor_count; i++)
{
Console.Error("Invalid CPU count returned");
return;
// Ignore hyperthreads/SMT. They're not helpful for pinning.
const cpuinfo_processor* proc = cpuinfo_get_processor(i);
if (!proc || proc->smt_id != 0)
continue;
processors.push_back(proc);
}
Console.WriteLn(Color_StrongYellow, "Processor count: %u cores, %u processors", cpuinfo_get_cores_count(),
cpuinfo_get_processors_count());
Console.WriteLn(Color_StrongYellow, "Cluster count: %u", cluster_count);
static std::vector<const cpuinfo_processor*> ordered_processors;
for (u32 i = 0; i < cluster_count; i++)
{
const cpuinfo_cluster* cluster = cpuinfo_get_cluster(i);
for (u32 j = 0; j < cluster->processor_count; j++)
{
const cpuinfo_processor* proc = cpuinfo_get_processor(cluster->processor_start + j);
if (!proc)
continue;
ordered_processors.push_back(proc);
}
}
// find the large and small clusters based on frequency
// this is assuming the large cluster is always clocked higher
// sort based on core, so that hyperthreads get pushed down
std::sort(ordered_processors.begin(), ordered_processors.end(),
// Prioritize faster cores in heterogeneous CPUs.
std::sort(processors.begin(), processors.end(),
[](const cpuinfo_processor* lhs, const cpuinfo_processor* rhs) {
return (lhs->core->frequency > rhs->core->frequency || lhs->smt_id < rhs->smt_id);
return (lhs->core->frequency > rhs->core->frequency);
});
s_processor_list.reserve(ordered_processors.size());
std::stringstream ss;
ss << "Ordered processor list: ";
for (const cpuinfo_processor* proc : ordered_processors)
SmallString str;
str.assign("Ordered processor list: ");
s_processor_list.reserve(processors.size());
for (const cpuinfo_processor* proc : processors)
{
if (proc != ordered_processors.front())
ss << ", ";
const u32 procid = GetProcessorIdForProcessor(proc);
ss << procid;
if (proc->smt_id != 0)
ss << "[SMT " << proc->smt_id << "]";
s_processor_list.push_back(procid);
const u32 proc_id = GetProcessorIdForProcessor(proc);
str.append_format("{}{}", (proc == processors.front()) ? "" : ", ", proc_id);
s_processor_list.push_back(proc_id);
}
Console.WriteLn(ss.str());
Console.WriteLn(str.view());
}
static void SetMTVUAndAffinityControlDefault(SettingsInterface& si)
void VMManager::SetHardwareDependentDefaultSettings(SettingsInterface& si)
{
VMManager::EnsureCPUInfoInitialized();
@ -3455,7 +3441,7 @@ static void InitializeProcessorList()
}
}
static void SetMTVUAndAffinityControlDefault(SettingsInterface& si)
void VMManager::SetHardwareDependentDefaultSettings(SettingsInterface& si)
{
VMManager::EnsureCPUInfoInitialized();
@ -3480,12 +3466,9 @@ static void InitializeProcessorList()
DevCon.WriteLn("(VMManager) InitializeCPUInfo() not implemented.");
}
static void SetMTVUAndAffinityControlDefault(SettingsInterface& si)
void VMManager::SetHardwareDependentDefaultSettings(SettingsInterface& si)
{
#ifdef __APPLE__
// Everything we support Mac-wise has enough cores for MTVU.
si.SetBoolValue("EmuCore/Speedhacks", "vuThread", true);
#endif
}
#endif
@ -3497,6 +3480,12 @@ void VMManager::EnsureCPUInfoInitialized()
void VMManager::SetEmuThreadAffinities()
{
const bool new_pin_enable = (GetState() != VMState::Shutdown && EmuConfig.EnableThreadPinning);
if (s_thread_affinities_set == new_pin_enable)
return;
s_thread_affinities_set = EmuConfig.EnableThreadPinning;
EnsureCPUInfoInitialized();
if (s_processor_list.empty())
@ -3505,45 +3494,33 @@ void VMManager::SetEmuThreadAffinities()
return;
}
if (EmuConfig.Cpu.AffinityControlMode == 0 || s_processor_list.size() < (EmuConfig.Speedhacks.vuThread ? 3 : 2))
const bool mtvu = EmuConfig.Speedhacks.vuThread;
if (!new_pin_enable || s_processor_list.size() < (mtvu ? 3 : 2))
{
if (EmuConfig.Cpu.AffinityControlMode != 0)
Console.Error("Insufficient processors for affinity control.");
if (new_pin_enable)
ERROR_LOG("Insufficient processors for thread pinning.");
MTGS::GetThreadHandle().SetAffinity(0);
vu1Thread.GetThreadHandle().SetAffinity(0);
s_vm_thread_handle.SetAffinity(0);
s_software_renderer_processor_list = {};
return;
}
static constexpr u8 processor_assignment[7][2][3] = {
//EE xx GS EE VU GS
{{0, 2, 1}, {0, 1, 2}}, // Disabled
{{0, 2, 1}, {0, 1, 2}}, // EE > VU > GS
{{0, 2, 1}, {0, 2, 1}}, // EE > GS > VU
{{0, 2, 1}, {1, 0, 2}}, // VU > EE > GS
{{1, 2, 0}, {2, 0, 1}}, // VU > GS > EE
{{1, 2, 0}, {1, 2, 0}}, // GS > EE > VU
{{1, 2, 0}, {2, 1, 0}}, // GS > VU > EE
};
// steal vu's thread if mtvu is off
const u8* this_proc_assigment =
processor_assignment[EmuConfig.Cpu.AffinityControlMode][EmuConfig.Speedhacks.vuThread];
const u32 ee_index = s_processor_list[this_proc_assigment[0]];
const u32 vu_index = s_processor_list[this_proc_assigment[1]];
const u32 gs_index = s_processor_list[this_proc_assigment[2]];
Console.WriteLn("Processor order assignment: EE=%u, VU=%u, GS=%u", this_proc_assigment[0], this_proc_assigment[1],
this_proc_assigment[2]);
const u32 ee_index = s_processor_list[0];
const u32 vu_index = s_processor_list[1];
const u32 gs_index = s_processor_list[mtvu ? 2 : 1];
INFO_LOG("Processor order assignment: EE={}, VU={}, GS={}", ee_index, vu_index, gs_index);
const u64 ee_affinity = static_cast<u64>(1) << ee_index;
Console.WriteLn(Color_StrongGreen, "EE thread is on processor %u (0x%llx)", ee_index, ee_affinity);
INFO_LOG(" EE thread is on processor {} (0x{:x})", ee_index, ee_affinity);
s_vm_thread_handle.SetAffinity(ee_affinity);
if (EmuConfig.Speedhacks.vuThread)
{
const u64 vu_affinity = static_cast<u64>(1) << vu_index;
Console.WriteLn(Color_StrongGreen, "VU thread is on processor %u (0x%llx)", vu_index, vu_affinity);
INFO_LOG(" VU thread is on processor {} (0x{:x})", vu_index, vu_affinity);
vu1Thread.GetThreadHandle().SetAffinity(vu_affinity);
}
else
@ -3552,19 +3529,33 @@ void VMManager::SetEmuThreadAffinities()
}
const u64 gs_affinity = static_cast<u64>(1) << gs_index;
Console.WriteLn(Color_StrongGreen, "GS thread is on processor %u (0x%llx)", gs_index, gs_affinity);
INFO_LOG(" GS thread is on processor {} (0x{:x})", gs_index, gs_affinity);
MTGS::GetThreadHandle().SetAffinity(gs_affinity);
// Try to find some threads for the software renderer.
// They should be in the same cluster as the main GS thread. If they're not, for example,
// we had 4 P cores and 6 E cores, let the OS schedule them instead.
s_software_renderer_processor_list.reserve(s_processor_list.size() - (mtvu ? 3 : 2));
const u32 gs_cluster_id = cpuinfo_get_processor(gs_index)->cluster->cluster_id;
for (size_t i = mtvu ? 3 : 2; i < s_processor_list.size(); i++)
{
const u32 proc_index = s_processor_list[i];
const u32 proc_cluster_id = cpuinfo_get_processor(proc_index)->cluster->cluster_id;
if (proc_cluster_id != gs_cluster_id)
{
WARNING_LOG(" Only using {} SW threads, processor {} is in cluster {}, but the GS thread is in cluster {}",
s_software_renderer_processor_list.size(), proc_index, proc_cluster_id, gs_cluster_id);
break;
}
s_software_renderer_processor_list.push_back(proc_index);
}
}
void VMManager::SetHardwareDependentDefaultSettings(SettingsInterface& si)
{
SetMTVUAndAffinityControlDefault(si);
}
const std::vector<u32>& VMManager::GetSortedProcessorList()
const std::vector<u32>& VMManager::Internal::GetSoftwareRendererProcessorList()
{
EnsureCPUInfoInitialized();
return s_processor_list;
return s_software_renderer_processor_list;
}
void VMManager::ReloadPINE()

View File

@ -224,10 +224,6 @@ namespace VMManager
/// Initializes default configuration in the specified file for the specified categories.
void SetDefaultSettings(SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui);
/// Returns a list of processors in the system, and their corresponding affinity mask.
/// This list is ordered by most performant to least performant for pinning threads to.
const std::vector<u32>& GetSortedProcessorList();
/// Returns the time elapsed in the current play session.
u64 GetSessionPlayedTime();
@ -285,6 +281,9 @@ namespace VMManager
/// Resets/clears all execution/code caches.
void ClearCPUExecutionCaches();
/// Returns a list of processors in the system, suitable for pinning for the software renderer.
const std::vector<u32>& GetSoftwareRendererProcessorList();
const std::string& GetELFOverride();
bool IsExecutionInterrupted();
void ELFLoadingOnCPUThread(std::string elf_path);