diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp
index 9142f3e38..c8fe8b9cc 100644
--- a/src/core/host_interface.cpp
+++ b/src/core/host_interface.cpp
@@ -473,6 +473,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetFloatValue("Main", "EmulationSpeed", 1.0f);
si.SetFloatValue("Main", "FastForwardSpeed", 0.0f);
si.SetFloatValue("Main", "TurboSpeed", 0.0f);
+ si.SetBoolValue("Main", "SyncToHostRefreshRate", true);
si.SetBoolValue("Main", "IncreaseTimerResolution", true);
si.SetBoolValue("Main", "StartPaused", false);
si.SetBoolValue("Main", "StartFullscreen", false);
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 27534d7ce..bcfd02b2e 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -112,6 +112,7 @@ void Settings::Load(SettingsInterface& si)
emulation_speed = si.GetFloatValue("Main", "EmulationSpeed", 1.0f);
fast_forward_speed = si.GetFloatValue("Main", "FastForwardSpeed", 0.0f);
turbo_speed = si.GetFloatValue("Main", "TurboSpeed", 0.0f);
+ sync_to_host_refresh_rate = si.GetBoolValue("Main", "SyncToHostRefreshRate", true);
increase_timer_resolution = si.GetBoolValue("Main", "IncreaseTimerResolution", true);
start_paused = si.GetBoolValue("Main", "StartPaused", false);
start_fullscreen = si.GetBoolValue("Main", "StartFullscreen", false);
@@ -283,6 +284,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetFloatValue("Main", "EmulationSpeed", emulation_speed);
si.SetFloatValue("Main", "FastForwardSpeed", fast_forward_speed);
si.SetFloatValue("Main", "TurboSpeed", turbo_speed);
+ si.SetBoolValue("Main", "SyncToHostRefreshRate", sync_to_host_refresh_rate);
si.SetBoolValue("Main", "IncreaseTimerResolution", increase_timer_resolution);
si.SetBoolValue("Main", "StartPaused", start_paused);
si.SetBoolValue("Main", "StartFullscreen", start_fullscreen);
diff --git a/src/core/settings.h b/src/core/settings.h
index c58f30995..64756595f 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -82,6 +82,7 @@ struct Settings
float emulation_speed = 1.0f;
float fast_forward_speed = 0.0f;
float turbo_speed = 0.0f;
+ bool sync_to_host_refresh_rate = true;
bool increase_timer_resolution = true;
bool start_paused = false;
bool start_fullscreen = false;
diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp
index f241878d0..8d66a9823 100644
--- a/src/duckstation-qt/consolesettingswidget.cpp
+++ b/src/duckstation-qt/consolesettingswidget.cpp
@@ -25,6 +25,8 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.region, "Console", "Region",
&Settings::ParseConsoleRegionName, &Settings::GetConsoleRegionName,
Settings::DEFAULT_CONSOLE_REGION);
+ SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.syncToHostRefreshRate, "Main",
+ "SyncToHostRefreshRate", true);
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU", "ExecutionMode",
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName,
Settings::DEFAULT_CPU_EXECUTION_MODE);
@@ -75,6 +77,10 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
dialog->registerWidgetHelp(
m_ui.turboSpeed, tr("Turbo Speed"), "100%",
tr("Sets the turbo speed. This speed will be used when the turbo hotkey is pressed/toggled."));
+ dialog->registerWidgetHelp(m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), "100%",
+ tr("Adjusts the emulation speed so the console's refresh rate matches the host's refresh "
+ "rate, when VSync is enabled. This results in the smoothest animations possible, at "
+ "the cost of potentially increasing the emulation speed by less than 1%."));
m_ui.cpuClockSpeed->setEnabled(m_ui.enableCPUClockSpeedControl->checkState() == Qt::Checked);
m_ui.cdromReadSpeedup->setCurrentIndex(m_host_interface->GetIntSettingValue("CDROM", "ReadSpeedup", 1) - 1);
diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui
index 51044c53e..a2da17111 100644
--- a/src/duckstation-qt/consolesettingswidget.ui
+++ b/src/duckstation-qt/consolesettingswidget.ui
@@ -81,6 +81,13 @@
-
+ -
+
+
+ Sync To Host Refresh Rate
+
+
+
diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp
index e612f694c..9d02e126d 100644
--- a/src/frontend-common/common_host_interface.cpp
+++ b/src/frontend-common/common_host_interface.cpp
@@ -604,26 +604,44 @@ bool CommonHostInterface::ResumeSystemFromMostRecentState()
void CommonHostInterface::UpdateSpeedLimiterState()
{
- const float target_speed = m_turbo_enabled ?
- g_settings.turbo_speed :
- (m_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
+ float target_speed = m_turbo_enabled ?
+ g_settings.turbo_speed :
+ (m_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
m_speed_limiter_enabled = (target_speed != 0.0f);
+ if (g_settings.sync_to_host_refresh_rate && target_speed == 1.0f && g_settings.video_sync_enabled && m_display &&
+ System::IsRunning())
+ {
+ float host_refresh_rate;
+ if (m_display->GetHostRefreshRate(&host_refresh_rate))
+ {
+ const float ratio = host_refresh_rate / System::GetThrottleFrequency();
+ const bool can_sync = (ratio >= 0.95f && ratio <= 1.05f);
+ Log_InfoPrintf("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s", host_refresh_rate,
+ System::GetThrottleFrequency(), ratio, can_sync ? "can sync" : "can't sync");
+ if (can_sync)
+ target_speed *= ratio;
+ }
+ }
+
const bool is_non_standard_speed = (std::abs(target_speed - 1.0f) > 0.05f);
const bool audio_sync_enabled =
!System::IsRunning() || (m_speed_limiter_enabled && g_settings.audio_sync_enabled && !is_non_standard_speed);
const bool video_sync_enabled =
!System::IsRunning() || (m_speed_limiter_enabled && g_settings.video_sync_enabled && !is_non_standard_speed);
const float max_display_fps = m_speed_limiter_enabled ? 0.0f : g_settings.display_max_fps;
+ Log_InfoPrintf("Target speed: %f%%", target_speed * 100.0f);
Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "",
(audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : ""));
Log_InfoPrintf("Max display fps: %f", max_display_fps);
if (m_audio_stream)
{
- const u32 input_sample_rate = (!is_non_standard_speed || target_speed == 0.0f || !g_settings.audio_resampling) ?
+ const u32 input_sample_rate = (target_speed == 0.0f || !g_settings.audio_resampling) ?
AUDIO_SAMPLE_RATE :
static_cast(static_cast(AUDIO_SAMPLE_RATE) * target_speed);
+ Log_InfoPrintf("Audio input sample rate: %u hz", input_sample_rate);
+
m_audio_stream->SetInputSampleRate(input_sample_rate);
m_audio_stream->SetOutputVolume(GetAudioOutputVolume());
m_audio_stream->SetSync(audio_sync_enabled);
@@ -2221,7 +2239,8 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings)
g_settings.emulation_speed != old_settings.emulation_speed ||
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
g_settings.display_max_fps != old_settings.display_max_fps ||
- g_settings.audio_resampling != old_settings.audio_resampling)
+ g_settings.audio_resampling != old_settings.audio_resampling ||
+ g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
{
UpdateSpeedLimiterState();
}