Path: blob/master/src/duckstation-qt/consolesettingswidget.cpp
4802 views
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#include "consolesettingswidget.h"4#include "qtutils.h"5#include "settingswindow.h"6#include "settingwidgetbinder.h"78#include "core/game_database.h"9#include "core/system.h"1011#include "util/cd_image.h"1213#include <QtWidgets/QMessageBox>14#include <QtWidgets/QPushButton>1516#include "moc_consolesettingswidget.cpp"1718static constexpr const int CDROM_SPEEDUP_VALUES[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0};1920ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* parent)21: QWidget(parent), m_dialog(dialog)22{23SettingsInterface* sif = dialog->getSettingsInterface();2425m_ui.setupUi(this);2627SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.region, "Console", "Region", &Settings::ParseConsoleRegionName,28&Settings::GetConsoleRegionName, &Settings::GetConsoleRegionDisplayName,29Settings::DEFAULT_CONSOLE_REGION, ConsoleRegion::Count,30&QtUtils::GetIconForRegion);31SettingWidgetBinder::BindWidgetToEnumSetting(32sif, m_ui.forceVideoTiming, "GPU", "ForceVideoTiming", &Settings::ParseForceVideoTimingName,33&Settings::GetForceVideoTimingName, &Settings::GetForceVideoTimingDisplayName,34Settings::DEFAULT_FORCE_VIDEO_TIMING_MODE, ForceVideoTimingMode::Count, +[](ForceVideoTimingMode mode) {35return QtUtils::GetIconForRegion(36(mode == ForceVideoTimingMode::Disabled) ?37ConsoleRegion::Auto :38((mode == ForceVideoTimingMode::NTSC) ? ConsoleRegion::NTSC_U : ConsoleRegion::PAL));39});40SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false);41SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardBoot, "BIOS", "FastForwardBoot", false);42SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false);43SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardMemoryCardAccess, "MemoryCards",44"FastForwardAccess", false);45connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &ConsoleSettingsWidget::onFastBootChanged);46onFastBootChanged();4748SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.cpuExecutionMode, "CPU", "ExecutionMode",49&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName,50&Settings::GetCPUExecutionModeDisplayName,51Settings::DEFAULT_CPU_EXECUTION_MODE, CPUExecutionMode::Count);52SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCPUClockSpeedControl, "CPU", "OverclockEnable", false);53SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.recompilerICache, "CPU", "RecompilerICache", false);54SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImageToRAM, "CDROM", "LoadImageToRAM", false);55SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromAutoDiscChange, "CDROM", "AutoDiscChange", false);5657if (!m_dialog->isPerGameSettings())58{59SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImagePatches, "CDROM", "LoadImagePatches", false);60SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromIgnoreDriveSubcode, "CDROM", "IgnoreHostSubcode",61false);62}63else64{65m_ui.cdromGridLayout->removeWidget(m_ui.cdromIgnoreDriveSubcode);66delete m_ui.cdromIgnoreDriveSubcode;67m_ui.cdromIgnoreDriveSubcode = nullptr;68m_ui.cdromGridLayout->removeWidget(m_ui.cdromLoadImagePatches);69delete m_ui.cdromLoadImagePatches;70m_ui.cdromLoadImagePatches = nullptr;71}7273SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromSeekSpeedup, "CDROM", "SeekSpeedup", 1,74CDROM_SPEEDUP_VALUES);75SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromReadSpeedup, "CDROM", "ReadSpeedup", 1,76CDROM_SPEEDUP_VALUES);7778dialog->registerWidgetHelp(m_ui.region, tr("Region"), tr("Auto-Detect"),79tr("Determines the emulated hardware type."));80dialog->registerWidgetHelp(81m_ui.forceVideoTiming, tr("Frame Rate"), tr("Auto-Detect"),82tr("Utilizes the chosen frame timing regardless of the active region. This feature can be used to force PAL games "83"to run at 60Hz and NTSC games to run at 50Hz. For most games which have a speed tied to the framerate, this "84"will result in the game running approximately 17% faster or slower. For variable frame rate games, it may not "85"affect the speed."));86m_dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),87tr("Skips the boot animation. Safe to enable."));88m_dialog->registerWidgetHelp(m_ui.fastForwardBoot, tr("Fast Forward Boot"), tr("Unchecked"),89tr("Fast forwards through the early loading process when fast booting, saving time. "90"Results may vary between games."));91dialog->registerWidgetHelp(92m_ui.enable8MBRAM, tr("Enable 8MB RAM (Dev Console)"), tr("Unchecked"),93tr("Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles. Games have "94"to use a larger heap size for "95"this additional RAM to be usable. Titles which rely on memory mirrors may break, so it should only be used "96"with compatible mods."));97m_dialog->registerWidgetHelp(m_ui.fastForwardMemoryCardAccess, tr("Fast Forward Memory Card Access"), tr("Unchecked"),98tr("Fast forwards through memory card access, both loading and saving. Can reduce "99"waiting times in games that frequently access memory cards."));100101dialog->registerWidgetHelp(m_ui.cpuExecutionMode, tr("Execution Mode"), tr("Recompiler (Fastest)"),102tr("Determines how the emulated CPU executes instructions."));103dialog->registerWidgetHelp(m_ui.enableCPUClockSpeedControl,104tr("Enable Clock Speed Control (Overclocking/Underclocking)"), tr("Unchecked"),105tr("When this option is chosen, the clock speed set below will be used."));106dialog->registerWidgetHelp(m_ui.cpuClockSpeed, tr("Overclocking Percentage"), tr("100%"),107tr("Selects the percentage of the normal clock speed the emulated hardware will run at."));108dialog->registerWidgetHelp(m_ui.recompilerICache, tr("Enable Recompiler ICache"), tr("Unchecked"),109tr("Simulates stalls in the recompilers when the emulated CPU would have to fetch "110"instructions into its cache. Makes games run closer to their console framerate, at a "111"small cost to performance. Interpreter mode always simulates the instruction cache."));112113dialog->registerWidgetHelp(114m_ui.cdromReadSpeedup, tr("CD-ROM Read Speedup"), tr("None (Double Speed)"),115tr("Speeds up CD-ROM reads by the specified factor. Only applies to double-speed reads, and is ignored when audio "116"is playing. May improve loading speeds in some games, at the cost of breaking others."));117dialog->registerWidgetHelp(118m_ui.cdromSeekSpeedup, tr("CD-ROM Seek Speedup"), tr("None (Normal Speed)"),119tr("Reduces the simulated time for the CD-ROM sled to move to different areas of the disc. Can improve loading "120"times, but crash games which do not expect the CD-ROM to operate faster."));121dialog->registerWidgetHelp(122m_ui.cdromLoadImageToRAM, tr("Preload Image to RAM"), tr("Unchecked"),123tr("Loads the game image into RAM. Useful for network paths that may become unreliable during gameplay. In some "124"cases also eliminates stutter when games initiate audio track playback."));125dialog->registerWidgetHelp(m_ui.cdromLoadImagePatches, tr("Apply Image Patches"), tr("Unchecked"),126tr("Automatically applies patches to disc images when they are present in the same "127"directory. Currently only PPF patches are supported with this option."));128dialog->registerWidgetHelp(129m_ui.cdromAutoDiscChange, tr("Switch to Next Disc on Stop"), tr("Unchecked"),130tr("Automatically switches to the next disc in the game when the game stops the CD-ROM motor. No switch will occur "131"if the last disc in the game is already selected. <strong>Does not work for all games.</strong>"));132dialog->registerWidgetHelp(133m_ui.cdromIgnoreDriveSubcode, tr("Ignore Drive Subcode"), tr("Unchecked"),134tr("Ignores the subchannel provided by the drive when using physical discs, instead always generating subchannel "135"data. Won't work with libcrypt games, but can improve read reliability on some drives."));136137m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false));138139connect(m_ui.enableCPUClockSpeedControl, &QCheckBox::checkStateChanged, this,140&ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked);141connect(m_ui.cpuClockSpeed, &QSlider::valueChanged, this, &ConsoleSettingsWidget::onCPUClockSpeedValueChanged);142143SettingWidgetBinder::SetAvailability(m_ui.fastBoot, !m_dialog->hasGameTrait(GameDatabase::Trait::ForceFullBoot));144SettingWidgetBinder::SetAvailability(m_ui.fastForwardBoot,145!m_dialog->hasGameTrait(GameDatabase::Trait::ForceFullBoot));146SettingWidgetBinder::SetAvailability(147m_ui.cpuExecutionMode, !m_dialog->hasGameTrait(GameDatabase::Trait::ForceInterpreter), m_ui.cpuExecutionModeLabel);148SettingWidgetBinder::SetAvailability(m_ui.cdromReadSpeedup,149!m_dialog->hasGameTrait(GameDatabase::Trait::DisableCDROMReadSpeedup),150m_ui.cdromReadSpeedupLabel);151SettingWidgetBinder::SetAvailability(m_ui.cdromSeekSpeedup,152!m_dialog->hasGameTrait(GameDatabase::Trait::DisableCDROMSeekSpeedup),153m_ui.cdromSeekSpeedupLabel);154SettingWidgetBinder::SetForceEnabled(m_ui.recompilerICache,155m_dialog->hasGameTrait(GameDatabase::Trait::ForceRecompilerICache));156157calculateCPUClockValue();158}159160ConsoleSettingsWidget::~ConsoleSettingsWidget() = default;161162void ConsoleSettingsWidget::onFastBootChanged()163{164const bool fast_boot_enabled =165m_dialog->getEffectiveBoolValue("BIOS", "PatchFastBoot", Settings::DEFAULT_FAST_BOOT_VALUE);166m_ui.fastForwardBoot->setEnabled(fast_boot_enabled);167}168169void ConsoleSettingsWidget::updateRecompilerICacheEnabled()170{171const CPUExecutionMode mode =172Settings::ParseCPUExecutionMode(173m_dialog174->getEffectiveStringValue("CPU", "ExecutionMode",175Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE))176.c_str())177.value_or(Settings::DEFAULT_CPU_EXECUTION_MODE);178m_ui.recompilerICache->setEnabled(mode != CPUExecutionMode::Interpreter &&179!m_dialog->hasGameTrait(GameDatabase::Trait::ForceRecompilerICache));180}181182void ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked(int state)183{184if (state == Qt::Checked &&185(!m_dialog->isPerGameSettings() || !Host::GetBaseBoolSettingValue("CPU", "OverclockEnable", false)) &&186!Host::GetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", false))187{188const QString message =189tr("Enabling CPU overclocking will break games, cause bugs, reduce performance and can significantly increase "190"system requirements.\n\nBy enabling this option you are agreeing to not create any bug reports unless you "191"have confirmed the bug also occurs with overclocking disabled.\n\nThis warning will only be shown once.");192193QMessageBox* mb =194new QMessageBox(QMessageBox::Warning, tr("CPU Overclocking Warning"), message, QMessageBox::NoButton, this);195mb->setAttribute(Qt::WA_DeleteOnClose, true);196mb->setWindowModality(Qt::WindowModal);197const QPushButton* const yes_button =198mb->addButton(tr("Yes, I will confirm bugs without overclocking before reporting."), QMessageBox::YesRole);199const QPushButton* const no_button = mb->addButton(tr("No, take me back to safety."), QMessageBox::NoRole);200connect(no_button, &QPushButton::clicked, this, [this]() {201QSignalBlocker sb(m_ui.enableCPUClockSpeedControl);202if (m_dialog->isPerGameSettings())203{204m_ui.enableCPUClockSpeedControl->setCheckState(Qt::PartiallyChecked);205m_dialog->removeSettingValue("CPU", "OverclockEnable");206}207else208{209m_ui.enableCPUClockSpeedControl->setCheckState(Qt::Unchecked);210m_dialog->setBoolSettingValue("CPU", "OverclockEnable", false);211}212213m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false));214updateCPUClockSpeedLabel();215});216connect(yes_button, &QPushButton::clicked, this, []() {217Host::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true);218Host::CommitBaseSettingChanges();219});220mb->show();221}222223m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false));224updateCPUClockSpeedLabel();225}226227void ConsoleSettingsWidget::onCPUClockSpeedValueChanged(int value)228{229const u32 percent = static_cast<u32>(m_ui.cpuClockSpeed->value());230u32 numerator, denominator;231Settings::CPUOverclockPercentToFraction(percent, &numerator, &denominator);232m_dialog->setIntSettingValue("CPU", "OverclockNumerator", static_cast<int>(numerator));233m_dialog->setIntSettingValue("CPU", "OverclockDenominator", static_cast<int>(denominator));234updateCPUClockSpeedLabel();235}236237void ConsoleSettingsWidget::updateCPUClockSpeedLabel()238{239const int percent = m_ui.enableCPUClockSpeedControl->isChecked() ? m_ui.cpuClockSpeed->value() : 100;240const double frequency = (static_cast<double>(System::MASTER_CLOCK) * static_cast<double>(percent)) / 100.0;241m_ui.cpuClockSpeedLabel->setText(tr("%1% (%2MHz)").arg(percent).arg(frequency / 1000000.0, 0, 'f', 2));242}243244void ConsoleSettingsWidget::calculateCPUClockValue()245{246const u32 numerator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockNumerator", 1));247const u32 denominator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockDenominator", 1));248const u32 percent = Settings::CPUOverclockFractionToPercent(numerator, denominator);249QSignalBlocker sb(m_ui.cpuClockSpeed);250m_ui.cpuClockSpeed->setValue(static_cast<int>(percent));251updateCPUClockSpeedLabel();252}253254255