consolesettingswidget.cpp (12441B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "consolesettingswidget.h" 5 #include "qtutils.h" 6 #include "settingswindow.h" 7 #include "settingwidgetbinder.h" 8 9 #include "core/game_database.h" 10 #include "core/system.h" 11 12 #include "util/cd_image.h" 13 14 #include <QtWidgets/QMessageBox> 15 #include <QtWidgets/QPushButton> 16 17 ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* parent) 18 : QWidget(parent), m_dialog(dialog) 19 { 20 SettingsInterface* sif = dialog->getSettingsInterface(); 21 22 m_ui.setupUi(this); 23 24 for (u32 i = 0; i < static_cast<u32>(ConsoleRegion::Count); i++) 25 m_ui.region->addItem(QString::fromUtf8(Settings::GetConsoleRegionDisplayName(static_cast<ConsoleRegion>(i)))); 26 27 for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++) 28 { 29 m_ui.cpuExecutionMode->addItem( 30 QString::fromUtf8(Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i)))); 31 } 32 33 static constexpr float TIME_PER_SECTOR_DOUBLE_SPEED = 1000.0f / 150.0f; 34 m_ui.cdromReadaheadSectors->addItem(tr("Disabled (Synchronous)")); 35 for (u32 i = 1; i <= 32; i++) 36 { 37 m_ui.cdromReadaheadSectors->addItem(tr("%1 sectors (%2 KB / %3 ms)") 38 .arg(i) 39 40 .arg(static_cast<float>(i) * TIME_PER_SECTOR_DOUBLE_SPEED, 0, 'f', 0) 41 .arg(static_cast<float>(i * CDImage::DATA_SECTOR_SIZE) / 1024.0f)); 42 } 43 44 SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.region, "Console", "Region", &Settings::ParseConsoleRegionName, 45 &Settings::GetConsoleRegionName, Settings::DEFAULT_CONSOLE_REGION); 46 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false); 47 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableAllEnhancements, "Main", "DisableAllEnhancements", 48 false); 49 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "Console", "EnableCheats", false); 50 SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.cpuExecutionMode, "CPU", "ExecutionMode", 51 &Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName, 52 Settings::DEFAULT_CPU_EXECUTION_MODE); 53 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCPUClockSpeedControl, "CPU", "OverclockEnable", false); 54 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.recompilerICache, "CPU", "RecompilerICache", false); 55 SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromReadaheadSectors, "CDROM", "ReadaheadSectors", 56 Settings::DEFAULT_CDROM_READAHEAD_SECTORS); 57 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImageToRAM, "CDROM", "LoadImageToRAM", false); 58 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImagePatches, "CDROM", "LoadImagePatches", false); 59 60 if (!m_dialog->isPerGameSettings()) 61 { 62 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromIgnoreDriveSubcode, "CDROM", "IgnoreHostSubcode", 63 false); 64 } 65 else 66 { 67 m_ui.cdromIgnoreDriveSubcode->setEnabled(false); 68 } 69 70 SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromSeekSpeedup, "CDROM", "SeekSpeedup", 1); 71 SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromReadSpeedup, "CDROM", "ReadSpeedup", 1, 1); 72 73 dialog->registerWidgetHelp(m_ui.region, tr("Region"), tr("Auto-Detect"), 74 tr("Determines the emulated hardware type.")); 75 dialog->registerWidgetHelp(m_ui.cpuExecutionMode, tr("Execution Mode"), tr("Recompiler (Fastest)"), 76 tr("Determines how the emulated CPU executes instructions.")); 77 dialog->registerWidgetHelp(m_ui.enableCPUClockSpeedControl, 78 tr("Enable Clock Speed Control (Overclocking/Underclocking)"), tr("Unchecked"), 79 tr("When this option is chosen, the clock speed set below will be used.")); 80 dialog->registerWidgetHelp(m_ui.cpuClockSpeed, tr("Overclocking Percentage"), tr("100%"), 81 tr("Selects the percentage of the normal clock speed the emulated hardware will run at.")); 82 dialog->registerWidgetHelp(m_ui.recompilerICache, tr("Enable Recompiler ICache"), tr("Unchecked"), 83 tr("Simulates stalls in the recompilers when the emulated CPU would have to fetch " 84 "instructions into its cache. Makes games run closer to their console framerate, at a " 85 "small cost to performance. Interpreter mode always simulates the instruction cache.")); 86 dialog->registerWidgetHelp( 87 m_ui.enable8MBRAM, tr("Enable 8MB RAM (Dev Console)"), tr("Unchecked"), 88 tr("Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles. Games have " 89 "to use a larger heap size for " 90 "this additional RAM to be usable. Titles which rely on memory mirrors may break, so it should only be used " 91 "with compatible mods.")); 92 dialog->registerWidgetHelp(m_ui.disableAllEnhancements, tr("Disable All Enhancements"), tr("Unchecked"), 93 tr("Disables all enhancement options, simulating the system as accurately as possible. " 94 "Use to quickly determine whether an enhancement is responsible for game bugs.")); 95 dialog->registerWidgetHelp( 96 m_ui.cdromLoadImageToRAM, tr("Preload Image to RAM"), tr("Unchecked"), 97 tr("Loads the game image into RAM. Useful for network paths that may become unreliable during gameplay. In some " 98 "cases also eliminates stutter when games initiate audio track playback.")); 99 dialog->registerWidgetHelp( 100 m_ui.cdromReadSpeedup, tr("CD-ROM Read Speedup"), tr("None (Double Speed)"), 101 tr("Speeds up CD-ROM reads by the specified factor. Only applies to double-speed reads, and is ignored when audio " 102 "is playing. May improve loading speeds in some games, at the cost of breaking others.")); 103 dialog->registerWidgetHelp( 104 m_ui.cdromSeekSpeedup, tr("CD-ROM Seek Speedup"), tr("None (Normal Speed)"), 105 tr("Reduces the simulated time for the CD-ROM sled to move to different areas of the disc. Can improve loading " 106 "times, but crash games which do not expect the CD-ROM to operate faster.")); 107 dialog->registerWidgetHelp(m_ui.cdromReadaheadSectors, tr("Asynchronous Readahead"), tr("8 Sectors"), 108 tr("Reduces hitches in emulation by reading/decompressing CD data asynchronously on a " 109 "worker thread. Higher sector numbers can reduce spikes when streaming FMVs or audio " 110 "on slower storage or when using compression formats such as CHD.")); 111 dialog->registerWidgetHelp( 112 m_ui.cdromLoadImageToRAM, tr("Preload Image to RAM"), tr("Unchecked"), 113 tr("Loads the game image into RAM. Useful for network paths that may become unreliable during gameplay. In some " 114 "cases also eliminates stutter when games initiate audio track playback.")); 115 dialog->registerWidgetHelp(m_ui.cdromLoadImagePatches, tr("Apply Image Patches"), tr("Unchecked"), 116 tr("Automatically applies patches to disc images when they are present in the same " 117 "directory. Currently only PPF patches are supported with this option.")); 118 dialog->registerWidgetHelp( 119 m_ui.cdromIgnoreDriveSubcode, tr("Ignore Drive Subcode"), tr("Unchecked"), 120 tr("Ignores the subchannel provided by the drive when using physical discs, instead always generating subchannel " 121 "data. Won't work with libcrypt games, but can improve read reliability on some drives.")); 122 123 m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false)); 124 125 connect(m_ui.enableCPUClockSpeedControl, &QCheckBox::checkStateChanged, this, 126 &ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked); 127 connect(m_ui.cpuClockSpeed, &QSlider::valueChanged, this, &ConsoleSettingsWidget::onCPUClockSpeedValueChanged); 128 129 SettingWidgetBinder::SetAvailability(m_ui.cpuExecutionModeLabel, 130 !m_dialog->hasGameTrait(GameDatabase::Trait::ForceInterpreter)); 131 SettingWidgetBinder::SetAvailability(m_ui.cpuExecutionMode, 132 !m_dialog->hasGameTrait(GameDatabase::Trait::ForceInterpreter)); 133 134 calculateCPUClockValue(); 135 } 136 137 ConsoleSettingsWidget::~ConsoleSettingsWidget() = default; 138 139 void ConsoleSettingsWidget::updateRecompilerICacheEnabled() 140 { 141 const CPUExecutionMode mode = 142 Settings::ParseCPUExecutionMode( 143 m_dialog 144 ->getEffectiveStringValue("CPU", "ExecutionMode", 145 Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE)) 146 .c_str()) 147 .value_or(Settings::DEFAULT_CPU_EXECUTION_MODE); 148 m_ui.recompilerICache->setEnabled(mode != CPUExecutionMode::Interpreter); 149 } 150 151 void ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked(int state) 152 { 153 if (state == Qt::Checked && 154 (!m_dialog->isPerGameSettings() || !Host::GetBaseBoolSettingValue("CPU", "OverclockEnable", false)) && 155 !Host::GetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", false)) 156 { 157 const QString message = 158 tr("Enabling CPU overclocking will break games, cause bugs, reduce performance and can significantly increase " 159 "system requirements.\n\nBy enabling this option you are agreeing to not create any bug reports unless you " 160 "have confirmed the bug also occurs with overclocking disabled.\n\nThis warning will only be shown once."); 161 162 QMessageBox mb(QMessageBox::Warning, tr("CPU Overclocking Warning"), message, QMessageBox::NoButton, this); 163 mb.setWindowModality(Qt::WindowModal); 164 const QAbstractButton* const yes_button = 165 mb.addButton(tr("Yes, I will confirm bugs without overclocking before reporting."), QMessageBox::YesRole); 166 mb.addButton(tr("No, take me back to safety."), QMessageBox::NoRole); 167 mb.exec(); 168 169 if (mb.clickedButton() != yes_button) 170 { 171 QSignalBlocker sb(m_ui.enableCPUClockSpeedControl); 172 if (m_dialog->isPerGameSettings()) 173 { 174 m_ui.enableCPUClockSpeedControl->setCheckState(Qt::PartiallyChecked); 175 m_dialog->removeSettingValue("CPU", "OverclockEnable"); 176 } 177 else 178 { 179 m_ui.enableCPUClockSpeedControl->setCheckState(Qt::Unchecked); 180 m_dialog->setBoolSettingValue("CPU", "OverclockEnable", false); 181 } 182 183 return; 184 } 185 186 Host::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true); 187 Host::CommitBaseSettingChanges(); 188 } 189 190 m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false)); 191 updateCPUClockSpeedLabel(); 192 } 193 194 void ConsoleSettingsWidget::onCPUClockSpeedValueChanged(int value) 195 { 196 const u32 percent = static_cast<u32>(m_ui.cpuClockSpeed->value()); 197 u32 numerator, denominator; 198 Settings::CPUOverclockPercentToFraction(percent, &numerator, &denominator); 199 m_dialog->setIntSettingValue("CPU", "OverclockNumerator", static_cast<int>(numerator)); 200 m_dialog->setIntSettingValue("CPU", "OverclockDenominator", static_cast<int>(denominator)); 201 updateCPUClockSpeedLabel(); 202 } 203 204 void ConsoleSettingsWidget::updateCPUClockSpeedLabel() 205 { 206 const int percent = m_ui.enableCPUClockSpeedControl->isChecked() ? m_ui.cpuClockSpeed->value() : 100; 207 const double frequency = (static_cast<double>(System::MASTER_CLOCK) * static_cast<double>(percent)) / 100.0; 208 m_ui.cpuClockSpeedLabel->setText(tr("%1% (%2MHz)").arg(percent).arg(frequency / 1000000.0, 0, 'f', 2)); 209 } 210 211 void ConsoleSettingsWidget::calculateCPUClockValue() 212 { 213 const u32 numerator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockNumerator", 1)); 214 const u32 denominator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockDenominator", 1)); 215 const u32 percent = Settings::CPUOverclockFractionToPercent(numerator, denominator); 216 QSignalBlocker sb(m_ui.cpuClockSpeed); 217 m_ui.cpuClockSpeed->setValue(static_cast<int>(percent)); 218 updateCPUClockSpeedLabel(); 219 }