2024-07-30 11:42:36 +00:00
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
2021-12-13 12:12:54 +00:00
2022-02-15 14:29:18 +00:00
# include <QtWidgets/QInputDialog>
2021-12-13 12:12:54 +00:00
# include <QtWidgets/QMessageBox>
# include <limits>
2023-05-13 04:30:41 +00:00
# include "pcsx2/Host.h"
2022-05-24 12:37:44 +00:00
2021-12-13 12:12:54 +00:00
# include "EmulationSettingsWidget.h"
# include "QtUtils.h"
# include "SettingWidgetBinder.h"
2023-10-14 08:45:09 +00:00
# include "SettingsWindow.h"
2021-12-13 12:12:54 +00:00
2022-11-23 13:20:49 +00:00
static constexpr int MINIMUM_EE_CYCLE_RATE = - 3 ;
static constexpr int MAXIMUM_EE_CYCLE_RATE = 3 ;
static constexpr int DEFAULT_EE_CYCLE_RATE = 0 ;
static constexpr int DEFAULT_EE_CYCLE_SKIP = 0 ;
2021-12-13 12:12:54 +00:00
static constexpr u32 DEFAULT_FRAME_LATENCY = 2 ;
2023-10-14 08:45:09 +00:00
EmulationSettingsWidget : : EmulationSettingsWidget ( SettingsWindow * dialog , QWidget * parent )
2021-12-13 12:12:54 +00:00
: QWidget ( parent )
2022-02-15 14:29:18 +00:00
, m_dialog ( dialog )
2021-12-13 12:12:54 +00:00
{
2022-02-15 14:29:18 +00:00
SettingsInterface * sif = dialog - > getSettingsInterface ( ) ;
2021-12-13 12:12:54 +00:00
m_ui . setupUi ( this ) ;
2022-02-15 14:29:18 +00:00
initializeSpeedCombo ( m_ui . normalSpeed , " Framerate " , " NominalScalar " , 1.0f ) ;
initializeSpeedCombo ( m_ui . fastForwardSpeed , " Framerate " , " TurboScalar " , 2.0f ) ;
initializeSpeedCombo ( m_ui . slowMotionSpeed , " Framerate " , " SlomoScalar " , 0.5f ) ;
2021-12-13 12:12:54 +00:00
2022-02-15 14:29:18 +00:00
SettingWidgetBinder : : BindWidgetToIntSetting ( sif , m_ui . maxFrameLatency , " EmuCore/GS " , " VsyncQueueSize " , DEFAULT_FRAME_LATENCY ) ;
2024-05-23 13:35:34 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . vsync , " EmuCore/GS " , " VsyncEnable " , false ) ;
2022-02-15 14:29:18 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . syncToHostRefreshRate , " EmuCore/GS " , " SyncToHostRefreshRate " , false ) ;
2024-05-23 13:35:34 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . useVSyncForTiming , " EmuCore/GS " , " UseVSyncForTiming " , false ) ;
2024-04-12 12:22:58 +00:00
connect ( m_ui . optimalFramePacing , & QCheckBox : : checkStateChanged , this , & EmulationSettingsWidget : : onOptimalFramePacingChanged ) ;
2024-05-23 13:35:34 +00:00
connect ( m_ui . vsync , & QCheckBox : : checkStateChanged , this , & EmulationSettingsWidget : : updateUseVSyncForTimingEnabled ) ;
connect ( m_ui . syncToHostRefreshRate , & QCheckBox : : checkStateChanged , this , & EmulationSettingsWidget : : updateUseVSyncForTimingEnabled ) ;
2022-02-15 14:29:18 +00:00
m_ui . optimalFramePacing - > setTristate ( dialog - > isPerGameSettings ( ) ) ;
2021-12-13 12:12:54 +00:00
2022-11-23 13:20:49 +00:00
SettingWidgetBinder : : BindWidgetToIntSetting ( sif , m_ui . eeCycleSkipping , " EmuCore/Speedhacks " , " EECycleSkip " , DEFAULT_EE_CYCLE_SKIP ) ;
2022-02-15 14:29:18 +00:00
2022-11-23 13:20:49 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . MTVU , " EmuCore/Speedhacks " , " vuThread " , false ) ;
2024-06-15 13:34:50 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . threadPinning , " EmuCore " , " EnableThreadPinning " , false ) ;
2022-11-23 13:20:49 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . fastCDVD , " EmuCore/Speedhacks " , " fastCDVD " , false ) ;
2024-06-09 16:27:38 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . precacheCDVD , " EmuCore " , " CdvdPrecache " , false ) ;
2022-11-23 13:20:49 +00:00
if ( m_dialog - > isPerGameSettings ( ) )
{
m_ui . eeCycleRate - > insertItem (
0 , tr ( " Use Global Setting [%1] " )
. arg ( m_ui . eeCycleRate - > itemText (
std : : clamp ( Host : : GetBaseIntSettingValue ( " EmuCore/Speedhacks " , " EECycleRate " , DEFAULT_EE_CYCLE_RATE ) - MINIMUM_EE_CYCLE_RATE ,
0 , MAXIMUM_EE_CYCLE_RATE - MINIMUM_EE_CYCLE_RATE ) ) ) ) ;
2023-05-25 16:24:01 +00:00
// Disable cheats, use the cheats panel instead (move fastcvd up in its spot).
const int count = m_ui . systemSettingsLayout - > count ( ) ;
for ( int i = 0 ; i < count ; i + + )
{
QLayoutItem * item = m_ui . systemSettingsLayout - > itemAt ( i ) ;
if ( item & & item - > widget ( ) = = m_ui . cheats )
{
int row , col , rowSpan , colSpan ;
m_ui . systemSettingsLayout - > getItemPosition ( i , & row , & col , & rowSpan , & colSpan ) ;
delete m_ui . systemSettingsLayout - > takeAt ( i ) ;
m_ui . systemSettingsLayout - > removeWidget ( m_ui . fastCDVD ) ;
m_ui . systemSettingsLayout - > addWidget ( m_ui . fastCDVD , row , col ) ;
delete m_ui . cheats ;
m_ui . cheats = nullptr ;
break ;
}
}
2022-11-23 13:20:49 +00:00
}
else
2022-11-22 14:34:50 +00:00
{
2023-05-25 16:24:01 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . cheats , " EmuCore " , " EnableCheats " , false ) ;
2022-11-23 13:20:49 +00:00
// Allow for FastCDVD for per-game settings only
m_ui . systemSettingsLayout - > removeWidget ( m_ui . fastCDVD ) ;
m_ui . fastCDVD - > deleteLater ( ) ;
2022-11-22 14:34:50 +00:00
}
2022-11-23 13:20:49 +00:00
const std : : optional < int > cycle_rate =
m_dialog - > getIntValue ( " EmuCore/Speedhacks " , " EECycleRate " , sif ? std : : nullopt : std : : optional < int > ( DEFAULT_EE_CYCLE_RATE ) ) ;
m_ui . eeCycleRate - > setCurrentIndex ( cycle_rate . has_value ( ) ? ( std : : clamp ( cycle_rate . value ( ) , MINIMUM_EE_CYCLE_RATE , MAXIMUM_EE_CYCLE_RATE ) +
( 0 - MINIMUM_EE_CYCLE_RATE ) + static_cast < int > ( m_dialog - > isPerGameSettings ( ) ) ) :
0 ) ;
connect ( m_ui . eeCycleRate , QOverload < int > : : of ( & QComboBox : : currentIndexChanged ) , this , [ this ] ( int index ) {
std : : optional < int > value ;
if ( ! m_dialog - > isPerGameSettings ( ) | | index > 0 )
value = MINIMUM_EE_CYCLE_RATE + index - static_cast < int > ( m_dialog - > isPerGameSettings ( ) ) ;
m_dialog - > setIntSettingValue ( " EmuCore/Speedhacks " , " EECycleRate " , value ) ;
} ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . hostFilesystem , " EmuCore " , " HostFs " , false ) ;
2023-10-01 16:55:25 +00:00
dialog - > registerWidgetHelp ( m_ui . normalSpeed , tr ( " Normal Speed " ) , tr ( " 100% " ) ,
2021-12-13 12:12:54 +00:00
tr ( " Sets the target emulation speed. It is not guaranteed that this speed will be reached, "
" and if not, the emulator will run as fast as it can manage. " ) ) ;
2023-03-18 21:14:55 +00:00
//: The "User Preference" string will appear after the text "Recommended Value:"
2023-03-13 00:34:20 +00:00
dialog - > registerWidgetHelp ( m_ui . fastForwardSpeed , tr ( " Fast-Forward Speed " ) , tr ( " User Preference " ) ,
tr ( " Sets the fast-forward speed. This speed will be used when the fast-forward hotkey is pressed/toggled. " ) ) ;
2023-03-18 21:14:55 +00:00
//: The "User Preference" string will appear after the text "Recommended Value:"
2023-03-13 00:34:20 +00:00
dialog - > registerWidgetHelp ( m_ui . slowMotionSpeed , tr ( " Slow-Motion Speed " ) , tr ( " User Preference " ) ,
tr ( " Sets the slow-motion speed. This speed will be used when the slow-motion hotkey is pressed/toggled. " ) ) ;
2021-12-13 12:12:54 +00:00
2023-03-13 00:34:20 +00:00
dialog - > registerWidgetHelp ( m_ui . eeCycleRate , tr ( " EE Cycle Rate " ) , tr ( " 100% (Normal Speed) " ) ,
2022-11-23 13:20:49 +00:00
tr ( " Higher values may increase internal framerate in games, but will increase CPU requirements substantially. "
" Lower values will reduce the CPU load allowing lightweight games to run full speed on weaker CPUs. " ) ) ;
2023-03-13 00:34:20 +00:00
dialog - > registerWidgetHelp ( m_ui . eeCycleSkipping , tr ( " EE Cycle Skip " ) , tr ( " Disabled " ) ,
2022-11-23 13:20:49 +00:00
tr ( " Makes the emulated Emotion Engine skip cycles. "
2023-03-18 21:14:55 +00:00
//: SOTC = Shadow of the Colossus. A game's title, should not be translated unless an official translation exists.
2022-11-23 13:20:49 +00:00
" Helps a small subset of games like SOTC. Most of the time it's harmful to performance. " ) ) ;
2024-06-15 13:34:50 +00:00
dialog - > registerWidgetHelp ( m_ui . threadPinning , tr ( " Enable Thread Pinning " ) , tr ( " Unchecked " ) ,
2023-01-11 16:04:52 +00:00
tr ( " Sets the priority for specific threads in a specific order ignoring the system scheduler. "
2023-03-18 21:14:55 +00:00
//: P-Core = Performance Core, E-Core = Efficiency Core. See if Intel has official translations for these terms.
2023-03-13 00:34:20 +00:00
" 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 " ) ,
2023-04-12 16:24:48 +00:00
tr ( " Generally a speedup on CPUs with 4 or more cores. "
2022-11-23 13:20:49 +00:00
" Safe for most games, but a few are incompatible and may hang. " ) ) ;
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. " ) ) ;
2024-06-09 16:27:38 +00:00
dialog - > registerWidgetHelp ( m_ui . precacheCDVD , tr ( " Enable CDVD Precaching " ) , tr ( " Unchecked " ) ,
tr ( " Loads the disc image into RAM before starting the virtual machine. Can reduce stutter on systems with hard drives that "
" have long wake times, but significantly increases boot times. " ) ) ;
2022-06-27 13:07:19 +00:00
dialog - > registerWidgetHelp ( m_ui . cheats , tr ( " Enable Cheats " ) , tr ( " Unchecked " ) ,
tr ( " Automatically loads and applies cheats on game start. " ) ) ;
2022-07-05 23:30:19 +00:00
dialog - > registerWidgetHelp ( m_ui . hostFilesystem , tr ( " Enable Host Filesystem " ) , tr ( " Unchecked " ) ,
tr ( " Allows games and homebrew to access files / folders directly on the host computer. " ) ) ;
2022-06-27 13:07:19 +00:00
2022-05-24 08:42:36 +00:00
dialog - > registerWidgetHelp ( m_ui . optimalFramePacing , tr ( " Optimal Frame Pacing " ) , tr ( " Unchecked " ) ,
2023-02-18 01:58:32 +00:00
tr ( " Sets the VSync queue size to 0, making every frame be completed and presented by the GS before input is polled and the next frame begins. "
" Using this setting can reduce input lag at the cost of measurably higher CPU and GPU requirements. " ) ) ;
2022-05-24 08:42:36 +00:00
dialog - > registerWidgetHelp ( m_ui . maxFrameLatency , tr ( " Maximum Frame Latency " ) , tr ( " 2 Frames " ) ,
tr ( " Sets the maximum number of frames that can be queued up to the GS, before the CPU thread will wait for one of them to complete before continuing. "
" Higher values can assist with smoothing out irregular frame times, but add additional input lag. " ) ) ;
2024-05-23 13:35:34 +00:00
dialog - > registerWidgetHelp ( m_ui . syncToHostRefreshRate , tr ( " Sync to Host Refresh Rate " ) , tr ( " Unchecked " ) ,
2023-09-24 03:56:10 +00:00
tr ( " Speeds up emulation so that the guest refresh rate matches the host. This results in the smoothest animations possible, at the cost of "
2024-05-23 13:35:34 +00:00
" potentially increasing the emulation speed by less than 1%. Sync to Host Refresh Rate will not take effect if "
2022-11-23 13:20:49 +00:00
" the console's refresh rate is too far from the host's refresh rate. Users with variable refresh rate displays "
" should disable this option. " ) ) ;
2024-05-23 13:35:34 +00:00
dialog - > registerWidgetHelp ( m_ui . vsync , tr ( " Vertical Sync (VSync) " ) , tr ( " Unchecked " ) ,
tr ( " Enable this option to match PCSX2's refresh rate with your current monitor or screen. VSync is automatically disabled when "
" it is not possible (eg. running at non-100% speed). " ) ) ;
dialog - > registerWidgetHelp ( m_ui . useVSyncForTiming , tr ( " Use Host VSync Timing " ) , tr ( " Unchecked " ) ,
tr ( " When synchronizing with the host refresh rate, this option disable's PCSX2's internal frame timing, and uses the host instead. "
" Can result in smoother frame pacing, <strong>but at the cost of increased input latency</strong>. " ) ) ;
2022-08-01 05:32:21 +00:00
2021-12-13 12:12:54 +00:00
updateOptimalFramePacing ( ) ;
2024-05-23 13:35:34 +00:00
updateUseVSyncForTimingEnabled ( ) ;
2021-12-13 12:12:54 +00:00
}
EmulationSettingsWidget : : ~ EmulationSettingsWidget ( ) = default ;
2022-02-15 14:29:18 +00:00
void EmulationSettingsWidget : : initializeSpeedCombo ( QComboBox * cb , const char * section , const char * key , float default_value )
2021-12-13 12:12:54 +00:00
{
2022-05-24 12:37:44 +00:00
float value = Host : : GetBaseFloatSettingValue ( section , key , default_value ) ;
2022-02-15 14:29:18 +00:00
if ( m_dialog - > isPerGameSettings ( ) )
{
cb - > addItem ( tr ( " Use Global Setting [%1%] " ) . arg ( value * 100.0f , 0 , ' f ' , 0 ) ) ;
2023-09-04 11:14:39 +00:00
if ( ! m_dialog - > getSettingsInterface ( ) - > GetFloatValue ( section , key , & value ) )
2022-02-15 14:29:18 +00:00
{
// set to something without data
value = - 1.0f ;
cb - > setCurrentIndex ( 0 ) ;
}
}
2021-12-13 12:12:54 +00:00
2022-03-27 05:48:12 +00:00
static const int speeds [ ] = { 2 , 10 , 25 , 50 , 75 , 90 , 100 , 110 , 120 , 150 , 175 , 200 , 300 , 400 , 500 , 1000 } ;
2022-02-15 14:29:18 +00:00
for ( const int speed : speeds )
{
cb - > addItem ( tr ( " %1% [%2 FPS (NTSC) / %3 FPS (PAL)] " )
. arg ( speed )
. arg ( ( 60 * speed ) / 100 )
. arg ( ( 50 * speed ) / 100 ) ,
QVariant ( static_cast < float > ( speed ) / 100.0f ) ) ;
}
2023-03-18 21:14:55 +00:00
//: Every case that uses this particular string seems to refer to speeds: Normal Speed/Fast Forward Speed/Slow Motion Speed.
2022-02-15 14:29:18 +00:00
cb - > addItem ( tr ( " Unlimited " ) , QVariant ( 0.0f ) ) ;
const int custom_index = cb - > count ( ) ;
2023-03-18 21:14:55 +00:00
//: Every case that uses this particular string seems to refer to speeds: Normal Speed/Fast Forward Speed/Slow Motion Speed.
2022-02-15 14:29:18 +00:00
cb - > addItem ( tr ( " Custom " ) ) ;
if ( const int index = cb - > findData ( QVariant ( value ) ) ; index > = 0 )
{
cb - > setCurrentIndex ( index ) ;
}
else if ( value > 0.0f )
{
cb - > setItemText ( custom_index , tr ( " Custom [%1% / %2 FPS (NTSC) / %3 FPS (PAL)] " )
. arg ( value * 100 )
. arg ( 60 * value )
. arg ( 50 * value ) ) ;
cb - > setCurrentIndex ( custom_index ) ;
}
connect ( cb , QOverload < int > : : of ( & QComboBox : : currentIndexChanged ) , this ,
[ this , cb , section , key ] ( int index ) { handleSpeedComboChange ( cb , section , key ) ; } ) ;
2021-12-13 12:12:54 +00:00
}
2022-02-15 14:29:18 +00:00
void EmulationSettingsWidget : : handleSpeedComboChange ( QComboBox * cb , const char * section , const char * key )
2021-12-13 12:12:54 +00:00
{
2022-02-15 14:29:18 +00:00
const int custom_index = cb - > count ( ) - 1 ;
const int current_index = cb - > currentIndex ( ) ;
std : : optional < float > new_value ;
if ( current_index = = custom_index )
{
bool ok = false ;
const double custom_value = QInputDialog : : getDouble (
QtUtils : : GetRootWidget ( this ) , tr ( " Custom Speed " ) , tr ( " Enter Custom Speed " ) , cb - > currentData ( ) . toFloat ( ) , 0.0f , 5000.0f , 1 , & ok ) ;
if ( ! ok )
{
// we need to set back to the old value
float value = m_dialog - > getEffectiveFloatValue ( section , key , 1.0f ) ;
QSignalBlocker sb ( cb ) ;
if ( m_dialog - > isPerGameSettings ( ) & & ! m_dialog - > getSettingsInterface ( ) - > GetFloatValue ( section , key , & value ) )
cb - > setCurrentIndex ( 0 ) ;
else if ( const int index = cb - > findData ( QVariant ( value ) ) ; index > = 0 )
cb - > setCurrentIndex ( index ) ;
return ;
}
cb - > setItemText ( custom_index , tr ( " Custom [%1% / %2 FPS (NTSC) / %3 FPS (PAL)] " )
. arg ( custom_value )
. arg ( ( 60 * custom_value ) / 100 )
. arg ( ( 50 * custom_value ) / 100 ) ) ;
new_value = static_cast < float > ( custom_value / 100.0 ) ;
}
else if ( current_index > 0 | | ! m_dialog - > isPerGameSettings ( ) )
{
new_value = cb - > currentData ( ) . toFloat ( ) ;
}
m_dialog - > setFloatSettingValue ( section , key , new_value ) ;
2021-12-13 12:12:54 +00:00
}
2022-02-15 14:29:18 +00:00
void EmulationSettingsWidget : : onOptimalFramePacingChanged ( )
2021-12-13 12:12:54 +00:00
{
const QSignalBlocker sb ( m_ui . maxFrameLatency ) ;
2022-02-15 14:29:18 +00:00
std : : optional < int > value ;
2022-05-24 08:42:36 +00:00
bool optimal = false ;
2022-02-15 14:29:18 +00:00
if ( m_ui . optimalFramePacing - > checkState ( ) ! = Qt : : PartiallyChecked )
2022-05-24 08:42:36 +00:00
{
optimal = m_ui . optimalFramePacing - > isChecked ( ) ;
value = optimal ? 0 : DEFAULT_FRAME_LATENCY ;
}
else
{
value = m_dialog - > getEffectiveIntValue ( " EmuCore/GS " , " VsyncQueueSize " , DEFAULT_FRAME_LATENCY ) ;
optimal = ( value = = 0 ) ;
}
2022-02-15 14:29:18 +00:00
2022-05-24 08:42:36 +00:00
m_ui . maxFrameLatency - > setMinimum ( optimal ? 0 : 1 ) ;
m_ui . maxFrameLatency - > setValue ( optimal ? 0 : DEFAULT_FRAME_LATENCY ) ;
2022-02-15 14:29:18 +00:00
m_ui . maxFrameLatency - > setEnabled ( ! m_dialog - > isPerGameSettings ( ) & & ! m_ui . optimalFramePacing - > isChecked ( ) ) ;
2021-12-13 12:12:54 +00:00
2022-02-15 14:29:18 +00:00
m_dialog - > setIntSettingValue ( " EmuCore/GS " , " VsyncQueueSize " , value ) ;
2021-12-13 12:12:54 +00:00
}
void EmulationSettingsWidget : : updateOptimalFramePacing ( )
{
const QSignalBlocker sb ( m_ui . optimalFramePacing ) ;
const QSignalBlocker sb2 ( m_ui . maxFrameLatency ) ;
2022-02-15 14:29:18 +00:00
int value = m_dialog - > getEffectiveIntValue ( " EmuCore/GS " , " VsyncQueueSize " , DEFAULT_FRAME_LATENCY ) ;
bool optimal = ( value = = 0 ) ;
if ( m_dialog - > isPerGameSettings ( ) & & ! m_dialog - > getSettingsInterface ( ) - > GetIntValue ( " EmuCore/GS " , " VsyncQueueSize " , & value ) )
{
m_ui . optimalFramePacing - > setCheckState ( Qt : : PartiallyChecked ) ;
m_ui . maxFrameLatency - > setEnabled ( false ) ;
}
else
{
m_ui . optimalFramePacing - > setChecked ( optimal ) ;
m_ui . maxFrameLatency - > setEnabled ( ! optimal ) ;
}
2022-05-24 08:42:36 +00:00
m_ui . maxFrameLatency - > setMinimum ( optimal ? 0 : 1 ) ;
m_ui . maxFrameLatency - > setValue ( optimal ? 0 : value ) ;
2021-12-13 12:12:54 +00:00
}
2024-05-23 13:35:34 +00:00
void EmulationSettingsWidget : : updateUseVSyncForTimingEnabled ( )
{
const bool vsync = m_dialog - > getEffectiveBoolValue ( " EmuCore/GS " , " VsyncEnable " , false ) ;
const bool sync_to_host_refresh = m_dialog - > getEffectiveBoolValue ( " EmuCore/GS " , " SyncToHostRefreshRate " , false ) ;
m_ui . useVSyncForTiming - > setEnabled ( vsync & & sync_to_host_refresh ) ;
}