duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }