From e9446fa9dc3ba64c22f8e5bb611ad394b5729eda Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Sun, 29 Sep 2024 03:30:13 -0400 Subject: [PATCH] implement 3 configurable and toggleable framerate targets (#2159) This pr allows for configuring the framerate target and adds support for two other framerate targets, a "fastforward" and "slowmo" target which can be enabled via either a toggle or holding a button. this allows for supporting a more accurate framerate target and allows for users to slow down the speed of gameplay if they so desire --- src/frontend/qt_sdl/Config.cpp | 48 +++- src/frontend/qt_sdl/Config.h | 4 + src/frontend/qt_sdl/EmuInstance.cpp | 26 +- src/frontend/qt_sdl/EmuInstance.h | 12 +- src/frontend/qt_sdl/EmuInstanceInput.cpp | 7 +- src/frontend/qt_sdl/EmuThread.cpp | 43 ++-- .../qt_sdl/InputConfig/InputConfigDialog.h | 6 + .../qt_sdl/InterfaceSettingsDialog.cpp | 52 +++- src/frontend/qt_sdl/InterfaceSettingsDialog.h | 10 + .../qt_sdl/InterfaceSettingsDialog.ui | 236 +++++++++++++++--- src/frontend/qt_sdl/Window.cpp | 5 +- 11 files changed, 394 insertions(+), 55 deletions(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 580af72e..0a161b6f 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -55,7 +55,6 @@ DefaultList DefaultInts = {"Screen.VSyncInterval", 1}, {"3D.Renderer", renderer3D_Software}, {"3D.GL.ScaleFactor", 1}, - {"MaxFPS", 1000}, #ifdef JIT_ENABLED {"JIT.MaxBlockSize", 32}, #endif @@ -120,6 +119,13 @@ DefaultList DefaultStrings = {"Instance*.Firmware.Username", "melonDS"} }; +DefaultList DefaultDoubles = +{ + {"TargetFPS", 60.0}, + {"FastForwardFPS", 1000.0}, + {"SlowmoFPS", 30.0}, +}; + LegacyEntry LegacyFile[] = { {"Key_A", 0, "Keyboard.A", true}, @@ -153,7 +159,7 @@ LegacyEntry LegacyFile[] = {"HKKey_Pause", 0, "Keyboard.HK_Pause", true}, {"HKKey_Reset", 0, "Keyboard.HK_Reset", true}, {"HKKey_FastForward", 0, "Keyboard.HK_FastForward", true}, - {"HKKey_FastForwardToggle", 0, "Keyboard.HK_FastForwardToggle", true}, + {"HKKey_FastForwardToggle", 0, "Keyboard.HK_FrameLimitToggle", true}, {"HKKey_FullscreenToggle", 0, "Keyboard.HK_FullscreenToggle", true}, {"HKKey_SwapScreens", 0, "Keyboard.HK_SwapScreens", true}, {"HKKey_SwapScreenEmphasis", 0, "Keyboard.HK_SwapScreenEmphasis", true}, @@ -169,7 +175,7 @@ LegacyEntry LegacyFile[] = {"HKJoy_Pause", 0, "Joystick.HK_Pause", true}, {"HKJoy_Reset", 0, "Joystick.HK_Reset", true}, {"HKJoy_FastForward", 0, "Joystick.HK_FastForward", true}, - {"HKJoy_FastForwardToggle", 0, "Joystick.HK_FastForwardToggle", true}, + {"HKJoy_FastForwardToggle", 0, "Joystick.HK_FrameLimitToggle", true}, {"HKJoy_FullscreenToggle", 0, "Joystick.HK_FullscreenToggle", true}, {"HKJoy_SwapScreens", 0, "Joystick.HK_SwapScreens", true}, {"HKJoy_SwapScreenEmphasis", 0, "Joystick.HK_SwapScreenEmphasis", true}, @@ -434,6 +440,18 @@ std::string Array::GetString(const int id) return tval.as_string(); } +double Array::GetDouble(const int id) +{ + while (Data.size() < id+1) + Data.push_back(0.0); + + toml::value& tval = Data[id]; + if (!tval.is_floating()) + tval = 0.0; + + return tval.as_floating(); +} + void Array::SetInt(const int id, int val) { while (Data.size() < id+1) @@ -470,6 +488,15 @@ void Array::SetString(const int id, const std::string& val) tval = val; } +void Array::SetDouble(const int id, double val) +{ + while (Data.size() < id+1) + Data.push_back(0.0); + + toml::value& tval = Data[id]; + tval = val; +} + /*Table::Table()// : Data(toml::value()) { @@ -562,6 +589,15 @@ std::string Table::GetString(const std::string& path) return tval.as_string(); } +double Table::GetDouble(const std::string& path) +{ + toml::value& tval = ResolvePath(path); + if (!tval.is_floating()) + tval = FindDefault(path, 0.0, DefaultDoubles); + + return tval.as_floating(); +} + void Table::SetInt(const std::string& path, int val) { std::string rngkey = GetDefaultKey(PathPrefix+path); @@ -593,6 +629,12 @@ void Table::SetString(const std::string& path, const std::string& val) tval = val; } +void Table::SetDouble(const std::string& path, double val) +{ + toml::value& tval = ResolvePath(path); + tval = val; +} + toml::value& Table::ResolvePath(const std::string& path) { toml::value* ret = &Data; diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 4ca88b46..9e6d3ea4 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -61,11 +61,13 @@ public: int64_t GetInt64(const int id); bool GetBool(const int id); std::string GetString(const int id); + double GetDouble(const int id); void SetInt(const int id, int val); void SetInt64(const int id, int64_t val); void SetBool(const int id, bool val); void SetString(const int id, const std::string& val); + void SetDouble(const int id, double val); // convenience @@ -99,11 +101,13 @@ public: int64_t GetInt64(const std::string& path); bool GetBool(const std::string& path); std::string GetString(const std::string& path); + double GetDouble(const std::string& path); void SetInt(const std::string& path, int val); void SetInt64(const std::string& path, int64_t val); void SetBool(const std::string& path, bool val); void SetString(const std::string& path, const std::string& val); + void SetDouble(const std::string& path, double val); // convenience diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index e48f29a2..54bae531 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -88,7 +88,31 @@ EmuInstance::EmuInstance(int inst) : deleting(false), cheatsOn = localCfg.GetBool("EnableCheats"); doLimitFPS = globalCfg.GetBool("LimitFPS"); - maxFPS = globalCfg.GetInt("MaxFPS"); + + double val = globalCfg.GetDouble("TargetFPS"); + if (val == 0.0) + { + Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n"); + targetFPS = 1.0 / 60.0; + } + else targetFPS = 1.0 / val; + + val = globalCfg.GetDouble("FastForwardFPS"); + if (val == 0.0) + { + Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n"); + fastForwardFPS = 1.0 / 60.0; + } + else fastForwardFPS = 1.0 / val; + + val = globalCfg.GetDouble("SlowmoFPS"); + if (val == 0.0) + { + Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n"); + slowmoFPS = 1.0 / 60.0; + } + else slowmoFPS = 1.0 / val; + doAudioSync = globalCfg.GetBool("AudioSync"); mpAudioMode = globalCfg.GetInt("MP.AudioMode"); diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 39c187c2..04290f56 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -36,7 +36,7 @@ enum HK_Pause, HK_Reset, HK_FastForward, - HK_FastForwardToggle, + HK_FrameLimitToggle, HK_FullscreenToggle, HK_SwapScreens, HK_SwapScreenEmphasis, @@ -46,6 +46,9 @@ enum HK_PowerButton, HK_VolumeUp, HK_VolumeDown, + HK_SlowMo, + HK_FastForwardToggle, + HK_SlowMoToggle, HK_MAX }; @@ -252,7 +255,12 @@ public: std::unique_ptr firmwareSave; bool doLimitFPS; - int maxFPS; + double curFPS; + double targetFPS; + double fastForwardFPS; + double slowmoFPS; + bool fastForwardToggled; + bool slowmoToggled; bool doAudioSync; private: diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index ddaca8f0..bb06c242 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -47,7 +47,7 @@ const char* EmuInstance::hotkeyNames[HK_MAX] = "HK_Pause", "HK_Reset", "HK_FastForward", - "HK_FastForwardToggle", + "HK_FrameLimitToggle", "HK_FullscreenToggle", "HK_SwapScreens", "HK_SwapScreenEmphasis", @@ -56,7 +56,10 @@ const char* EmuInstance::hotkeyNames[HK_MAX] = "HK_FrameStep", "HK_PowerButton", "HK_VolumeUp", - "HK_VolumeDown" + "HK_VolumeDown", + "HK_SlowMo", + "HK_FastForwardToggle", + "HK_SlowMoToggle" }; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index ae66e1ba..4ce4efda 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -149,12 +149,17 @@ void EmuThread::run() char melontitle[100]; + bool fastforward = false; + bool slowmo = false; + emuInstance->fastForwardToggled = false; + emuInstance->slowmoToggled = false; + while (emuStatus != emuStatus_Exit) { MPInterface::Get().Process(); emuInstance->inputProcess(); - if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); + if (emuInstance->hotkeyPressed(HK_FrameLimitToggle)) emit windowLimitFPSChange(); if (emuInstance->hotkeyPressed(HK_Pause)) emuTogglePause(); if (emuInstance->hotkeyPressed(HK_Reset)) emuReset(); @@ -332,22 +337,34 @@ void EmuThread::run() emit windowUpdate(); winUpdateCount = 0; } + + if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emuInstance->fastForwardToggled = !emuInstance->fastForwardToggled; + if (emuInstance->hotkeyPressed(HK_SlowMoToggle)) emuInstance->slowmoToggled = !emuInstance->slowmoToggled; - bool fastforward = emuInstance->hotkeyDown(HK_FastForward); + bool enablefastforward = emuInstance->hotkeyDown(HK_FastForward) | emuInstance->fastForwardToggled; + bool enableslowmo = emuInstance->hotkeyDown(HK_SlowMo) | emuInstance->slowmoToggled; if (useOpenGL) { - // when using OpenGL: when toggling fast-forward, change the vsync interval - if (emuInstance->hotkeyPressed(HK_FastForward)) + // when using OpenGL: when toggling fast-forward or slowmo, change the vsync interval + if ((enablefastforward || enableslowmo) && !(fastforward || slowmo)) { emuInstance->setVSyncGL(false); } - else if (emuInstance->hotkeyReleased(HK_FastForward)) + else if (!(enablefastforward || enableslowmo) && (fastforward || slowmo)) { emuInstance->setVSyncGL(true); } } + fastforward = enablefastforward; + slowmo = enableslowmo; + + if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS; + else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS; + else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1.0 / 1000.0; + else emuInstance->curFPS = emuInstance->targetFPS; + if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1) { DSi* dsi = static_cast(emuInstance->nds); @@ -361,23 +378,19 @@ void EmuThread::run() emuInstance->audioVolume = volumeLevel * (256.0 / 31.0); } - if (emuInstance->doAudioSync && !fastforward) + if (emuInstance->doAudioSync && !(fastforward || slowmo)) emuInstance->audioSync(); double frametimeStep = nlines / (60.0 * 263.0); { - bool limitfps = emuInstance->doLimitFPS && !fastforward; - - double practicalFramelimit = limitfps ? frametimeStep : 1.0 / emuInstance->maxFPS; - double curtime = SDL_GetPerformanceCounter() * perfCountsSec; - frameLimitError += practicalFramelimit - (curtime - lastTime); - if (frameLimitError < -practicalFramelimit) - frameLimitError = -practicalFramelimit; - if (frameLimitError > practicalFramelimit) - frameLimitError = practicalFramelimit; + frameLimitError += emuInstance->curFPS - (curtime - lastTime); + if (frameLimitError < -emuInstance->curFPS) + frameLimitError = -emuInstance->curFPS; + if (frameLimitError > emuInstance->curFPS) + frameLimitError = emuInstance->curFPS; if (round(frameLimitError * 1000.0) > 0.0) { diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 64986d62..3337228f 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -49,6 +49,9 @@ static constexpr std::initializer_list hk_general = HK_FrameStep, HK_FastForward, HK_FastForwardToggle, + HK_SlowMo, + HK_SlowMoToggle, + HK_FrameLimitToggle, HK_FullscreenToggle, HK_Lid, HK_Mic, @@ -65,6 +68,9 @@ static constexpr std::initializer_list hk_general_labels = "Reset", "Frame step", "Fast forward", + "Toggle fast forward", + "Slow mo", + "Toggle slow mo", "Toggle FPS limit", "Toggle fullscreen", "Close/open lid", diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index dfe30d12..2e7e75c9 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -39,7 +39,9 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked()); ui->spinMouseHideSeconds->setValue(cfg.GetInt("MouseHideSeconds")); ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus")); - ui->spinMaxFPS->setValue(cfg.GetInt("MaxFPS")); + ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS")); + ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS")); + ui->spinSlow->setValue(cfg.GetDouble("SlowmoFPS")); const QList themeKeys = QStyleFactory::keys(); const QString currentTheme = qApp->style()->objectName(); @@ -65,6 +67,41 @@ void InterfaceSettingsDialog::on_cbMouseHide_clicked() ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked()); } +void InterfaceSettingsDialog::on_pbClean_clicked() +{ + ui->spinTargetFPS->setValue(60.0000); +} + +void InterfaceSettingsDialog::on_pbAccurate_clicked() +{ + ui->spinTargetFPS->setValue(59.8261); +} + +void InterfaceSettingsDialog::on_pb2x_clicked() +{ + ui->spinFFW->setValue(ui->spinTargetFPS->value() * 2.0); +} + +void InterfaceSettingsDialog::on_pb3x_clicked() +{ + ui->spinFFW->setValue(ui->spinTargetFPS->value() * 3.0); +} + +void InterfaceSettingsDialog::on_pbMAX_clicked() +{ + ui->spinFFW->setValue(1000.0); +} + +void InterfaceSettingsDialog::on_pbHalf_clicked() +{ + ui->spinSlow->setValue(ui->spinTargetFPS->value() / 2.0); +} + +void InterfaceSettingsDialog::on_pbQuarter_clicked() +{ + ui->spinSlow->setValue(ui->spinTargetFPS->value() / 4.0); +} + void InterfaceSettingsDialog::done(int r) { if (r == QDialog::Accepted) @@ -74,7 +111,18 @@ void InterfaceSettingsDialog::done(int r) cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked()); cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value()); cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked()); - cfg.SetInt("MaxFPS", ui->spinMaxFPS->value()); + + double val = ui->spinTargetFPS->value(); + if (val == 0.0) cfg.SetDouble("TargetFPS", 0.0001); + else cfg.SetDouble("TargetFPS", val); + + val = ui->spinFFW->value(); + if (val == 0.0) cfg.SetDouble("FastForwardFPS", 0.0001); + else cfg.SetDouble("FastForwardFPS", val); + + val = ui->spinSlow->value(); + if (val == 0.0) cfg.SetDouble("SlowmoFPS", 0.0001); + else cfg.SetDouble("SlowmoFPS", val); QString themeName = ui->cbxUITheme->currentData().toString(); cfg.SetQString("UITheme", themeName); diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.h b/src/frontend/qt_sdl/InterfaceSettingsDialog.h index bbc62df9..c960e560 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.h +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.h @@ -60,6 +60,16 @@ private slots: void on_cbMouseHide_clicked(); + void on_pbClean_clicked(); + void on_pbAccurate_clicked(); + + void on_pb2x_clicked(); + void on_pb3x_clicked(); + void on_pbMAX_clicked(); + + void on_pbHalf_clicked(); + void on_pbQuarter_clicked(); + private: Ui::InterfaceSettingsDialog* ui; diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui index 21d8434e..460a6e99 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui @@ -6,12 +6,12 @@ 0 0 - 337 - 275 + 389 + 356 - + 0 0 @@ -20,6 +20,9 @@ Interface settings - melonDS + + QLayout::SizeConstraint::SetFixedSize + @@ -95,32 +98,209 @@ Framerate - + - - - Fast-forward limit + + + 6 - - spinMaxFPS + + 2 - - - - - - FPS - - - 60 - - - 1000 - - - 1000 - - + + + + Target FPS + + + + + + + Fast-Forward + + + spinFFW + + + + + + + FPS + + + 4 + + + 0.000100000000000 + + + 1000.000000000000000 + + + 59.826099999999997 + + + + + + + 2 + + + + + + 63 + 16777215 + + + + 1/4 + + + + + + + + 62 + 16777215 + + + + 1/2 + + + + + + + + + FPS + + + 4 + + + 0.000100000000000 + + + 1000.000000000000000 + + + 29.913000000000000 + + + + + + + FPS + + + 4 + + + 0.000100000000000 + + + 1000.000000000000000 + + + 1000.000000000000000 + + + + + + + Slow-Mo + + + + + + + 2 + + + + + + 63 + 16777215 + + + + Accurate + + + + + + + + 62 + 16777215 + + + + Clean + + + + + + + + + 2 + + + + + + 41 + 16777215 + + + + 2x + + + + + + + + 41 + 16777215 + + + + 3x + + + + + + + + 41 + 16777215 + + + + MAX + + + + + + @@ -128,10 +308,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index d40a062e..3020defd 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -1946,8 +1946,9 @@ void MainWindow::onOpenInterfaceSettings() void MainWindow::onUpdateInterfaceSettings() { pauseOnLostFocus = globalCfg.GetBool("PauseLostFocus"); - emuInstance->maxFPS = globalCfg.GetInt("MaxFPS"); - + emuInstance->targetFPS = 1.0 / globalCfg.GetDouble("TargetFPS"); + emuInstance->fastForwardFPS = 1.0 / globalCfg.GetDouble("FastForwardFPS"); + emuInstance->slowmoFPS = 1.0 / globalCfg.GetDouble("SlowmoFPS"); panel->setMouseHide(globalCfg.GetBool("MouseHide"), globalCfg.GetInt("MouseHideSeconds")*1000); }