From 88270771da2cbcf9c76418bb671705cb22f5d82b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 11 Apr 2024 21:26:50 +1000 Subject: [PATCH] System: Combine VRR and Optimal Frame Pacing GSync/FreeSync display users should: - DISABLE VSync. - ENABLE Optimal Frame Pacing. --- src/core/fullscreen_ui.cpp | 18 +- src/core/host.cpp | 2 +- src/core/settings.cpp | 48 +----- src/core/settings.h | 9 +- src/core/system.cpp | 55 +++--- src/core/system.h | 2 +- .../emulationsettingswidget.cpp | 25 ++- src/duckstation-qt/emulationsettingswidget.h | 1 + src/duckstation-qt/emulationsettingswidget.ui | 19 ++- src/duckstation-qt/graphicssettingswidget.cpp | 13 -- src/duckstation-qt/graphicssettingswidget.ui | 160 ++++++++---------- src/duckstation-qt/qthost.cpp | 19 ++- src/util/d3d11_device.cpp | 6 +- src/util/d3d12_device.cpp | 2 +- src/util/gpu_device.cpp | 11 +- src/util/gpu_device.h | 12 +- src/util/gpu_types.h | 9 - src/util/metal_device.h | 2 +- src/util/metal_device.mm | 12 +- src/util/opengl_context_wgl.cpp | 2 +- src/util/opengl_device.cpp | 16 +- src/util/opengl_device.h | 2 +- src/util/vulkan_device.cpp | 15 +- src/util/vulkan_device.h | 2 +- src/util/vulkan_swap_chain.cpp | 41 ++--- src/util/vulkan_swap_chain.h | 10 +- 26 files changed, 224 insertions(+), 289 deletions(-) diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index cba59a561..6d268c249 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -4180,11 +4180,10 @@ void FullscreenUI::DrawDisplaySettingsPage() "GPU", "UseSoftwareRendererForReadbacks", false); } - DrawEnumSetting( - bsi, FSUI_CSTR("VSync"), - FSUI_CSTR("Synchronizes presentation of the console's frames to the host. Enable for smoother animations."), - "Display", "SyncMode", Settings::DEFAULT_DISPLAY_SYNC_MODE, &Settings::ParseDisplaySyncMode, - &Settings::GetDisplaySyncModeName, &Settings::GetDisplaySyncModeDisplayName, DisplaySyncMode::Count); + DrawToggleSetting(bsi, FSUI_CSTR("VSync"), + FSUI_CSTR("Synchronizes presentation of the console's frames to the host. GSync/FreeSync users " + "should enable Optimal Frame Pacing instead."), + "Display", "VSync", false); DrawToggleSetting( bsi, FSUI_CSTR("Sync To Host Refresh Rate"), @@ -4192,10 +4191,11 @@ void FullscreenUI::DrawDisplaySettingsPage() "Resampling are enabled."), "Main", "SyncToHostRefreshRate", false); - DrawToggleSetting(bsi, FSUI_CSTR("Optimal Frame Pacing"), - FSUI_CSTR("Ensures every frame generated is displayed for optimal pacing. Disable if you are " - "having speed or sound issues."), - "Display", "DisplayAllFrames", false); + DrawToggleSetting( + bsi, FSUI_CSTR("Optimal Frame Pacing"), + FSUI_CSTR("Ensures every frame generated is displayed for optimal pacing. Enable for variable refresh displays, " + "such as GSync/FreeSync. Disable if you are having speed or sound issues."), + "Display", "OptimalFramePacing", false); MenuHeading(FSUI_CSTR("Rendering")); diff --git a/src/core/host.cpp b/src/core/host.cpp index 91c30ebec..0e5b80fa8 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -296,7 +296,7 @@ bool Host::CreateGPUDevice(RenderAPI api) if (!g_gpu_device || !g_gpu_device->Create( g_settings.gpu_adapter, g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache), - SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::GetEffectiveDisplaySyncMode(), + SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::IsVSyncEffectivelyEnabled(), g_settings.gpu_threaded_presentation, exclusive_fullscreen_control, static_cast(disabled_features), &error)) { diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 71d06760e..df231496d 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -248,10 +248,6 @@ void Settings::Load(SettingsInterface& si) display_scaling = ParseDisplayScaling(si.GetStringValue("Display", "Scaling", GetDisplayScalingName(DEFAULT_DISPLAY_SCALING)).c_str()) .value_or(DEFAULT_DISPLAY_SCALING); - display_sync_mode = - ParseDisplaySyncMode( - si.GetStringValue("Display", "SyncMode", GetDisplaySyncModeName(DEFAULT_DISPLAY_SYNC_MODE)).c_str()) - .value_or(DEFAULT_DISPLAY_SYNC_MODE); display_exclusive_fullscreen_control = ParseDisplayExclusiveFullscreenControl( si.GetStringValue("Display", "ExclusiveFullscreenControl", @@ -270,6 +266,8 @@ void Settings::Load(SettingsInterface& si) .value_or(DEFAULT_DISPLAY_SCREENSHOT_FORMAT); display_screenshot_quality = static_cast( std::clamp(si.GetUIntValue("Display", "ScreenshotQuality", DEFAULT_DISPLAY_SCREENSHOT_QUALITY), 1, 100)); + display_optimal_frame_pacing = si.GetBoolValue("Display", "OptimalFramePacing", false); + display_vsync = si.GetBoolValue("Display", "VSync", false); display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false); display_active_start_offset = static_cast(si.GetIntValue("Display", "ActiveStartOffset", 0)); display_active_end_offset = static_cast(si.GetIntValue("Display", "ActiveEndOffset", 0)); @@ -286,7 +284,6 @@ void Settings::Load(SettingsInterface& si) display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true); display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false); display_show_enhancements = si.GetBoolValue("Display", "ShowEnhancements", false); - display_all_frames = si.GetBoolValue("Display", "DisplayAllFrames", false); display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false); display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS); display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE); @@ -523,7 +520,8 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio)); si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment)); si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling)); - si.SetStringValue("Display", "SyncMode", GetDisplaySyncModeName(display_sync_mode)); + si.SetBoolValue("Display", "OptimalFramePacing", display_optimal_frame_pacing); + si.SetBoolValue("Display", "VSync", display_vsync); si.SetStringValue("Display", "ExclusiveFullscreenControl", GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control)); si.SetStringValue("Display", "ScreenshotMode", GetDisplayScreenshotModeName(display_screenshot_mode)); @@ -547,7 +545,6 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetFloatValue("Display", "OSDScale", display_osd_scale); } - si.SetBoolValue("Display", "DisplayAllFrames", display_all_frames); si.SetBoolValue("Display", "StretchVertically", display_stretch_vertically); si.SetFloatValue("Display", "MaxFPS", display_max_fps); @@ -1452,43 +1449,6 @@ const char* Settings::GetDisplayScalingDisplayName(DisplayScalingMode mode) return Host::TranslateToCString("DisplayScalingMode", s_display_scaling_display_names[static_cast(mode)]); } -static constexpr const std::array s_display_sync_mode_names = { - "Disabled", - "VSync", - "VSyncRelaxed", - "VRR", -}; -static constexpr const std::array s_display_sync_mode_display_names = { - TRANSLATE_NOOP("Settings", "Disabled"), - TRANSLATE_NOOP("Settings", "VSync"), - TRANSLATE_NOOP("Settings", "Relaxed VSync"), - TRANSLATE_NOOP("Settings", "VRR/FreeSync/GSync"), -}; - -std::optional Settings::ParseDisplaySyncMode(const char* str) -{ - int index = 0; - for (const char* name : s_display_sync_mode_names) - { - if (StringUtil::Strcasecmp(name, str) == 0) - return static_cast(index); - - index++; - } - - return std::nullopt; -} - -const char* Settings::GetDisplaySyncModeName(DisplaySyncMode mode) -{ - return s_display_sync_mode_names[static_cast(mode)]; -} - -const char* Settings::GetDisplaySyncModeDisplayName(DisplaySyncMode mode) -{ - return Host::TranslateToCString("Settings", s_display_sync_mode_display_names[static_cast(mode)]); -} - static constexpr const std::array s_display_exclusive_fullscreen_mode_names = { "Automatic", "Disallowed", diff --git a/src/core/settings.h b/src/core/settings.h index 5723ffe8f..320120035 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -134,7 +134,6 @@ struct Settings DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO; DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT; DisplayScalingMode display_scaling = DEFAULT_DISPLAY_SCALING; - DisplaySyncMode display_sync_mode = DEFAULT_DISPLAY_SYNC_MODE; DisplayExclusiveFullscreenControl display_exclusive_fullscreen_control = DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL; DisplayScreenshotMode display_screenshot_mode = DEFAULT_DISPLAY_SCREENSHOT_MODE; DisplayScreenshotFormat display_screenshot_format = DEFAULT_DISPLAY_SCREENSHOT_FORMAT; @@ -145,6 +144,8 @@ struct Settings s16 display_active_end_offset = 0; s8 display_line_start_offset = 0; s8 display_line_end_offset = 0; + bool display_optimal_frame_pacing : 1 = false; + bool display_vsync : 1 = false; bool display_force_4_3_for_24bit : 1 = false; bool gpu_24bit_chroma_smoothing : 1 = false; bool display_show_osd_messages : 1 = true; @@ -158,7 +159,6 @@ struct Settings bool display_show_status_indicators : 1 = true; bool display_show_inputs : 1 = false; bool display_show_enhancements : 1 = false; - bool display_all_frames : 1 = false; bool display_stretch_vertically : 1 = false; float display_osd_scale = 100.0f; float display_max_fps = DEFAULT_DISPLAY_MAX_FPS; @@ -416,10 +416,6 @@ struct Settings static const char* GetDisplayScalingName(DisplayScalingMode mode); static const char* GetDisplayScalingDisplayName(DisplayScalingMode mode); - static std::optional ParseDisplaySyncMode(const char* str); - static const char* GetDisplaySyncModeName(DisplaySyncMode mode); - static const char* GetDisplaySyncModeDisplayName(DisplaySyncMode mode); - static std::optional ParseDisplayExclusiveFullscreenControl(const char* str); static const char* GetDisplayExclusiveFullscreenControlName(DisplayExclusiveFullscreenControl mode); static const char* GetDisplayExclusiveFullscreenControlDisplayName(DisplayExclusiveFullscreenControl mode); @@ -496,7 +492,6 @@ struct Settings static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto; static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center; static constexpr DisplayScalingMode DEFAULT_DISPLAY_SCALING = DisplayScalingMode::BilinearSmooth; - static constexpr DisplaySyncMode DEFAULT_DISPLAY_SYNC_MODE = DisplaySyncMode::Disabled; static constexpr DisplayExclusiveFullscreenControl DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL = DisplayExclusiveFullscreenControl::Automatic; static constexpr DisplayScreenshotMode DEFAULT_DISPLAY_SCREENSHOT_MODE = DisplayScreenshotMode::ScreenResolution; diff --git a/src/core/system.cpp b/src/core/system.cpp index 30a5663b2..95aac2905 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -166,8 +166,8 @@ static const GameDatabase::Entry* s_running_game_entry = nullptr; static System::GameHash s_running_game_hash; static bool s_was_fast_booted; -static float s_throttle_frequency = 60.0f; -static float s_target_speed = 1.0f; +static float s_throttle_frequency = 0.0f; +static float s_target_speed = 0.0f; static Common::Timer::Value s_frame_period = 0; static Common::Timer::Value s_next_frame_time = 0; static bool s_last_frame_skipped = false; @@ -177,8 +177,8 @@ static bool s_system_interrupted = false; static bool s_frame_step_request = false; static bool s_fast_forward_enabled = false; static bool s_turbo_enabled = false; -static bool s_throttler_enabled = true; -static bool s_display_all_frames = true; +static bool s_throttler_enabled = false; +static bool s_optimal_frame_pacing = false; static bool s_syncing_to_host = false; static float s_average_frame_time_accumulator = 0.0f; @@ -1862,13 +1862,11 @@ void System::FrameDone() } // TODO: Kick cmdbuffer early - const DisplaySyncMode sync_mode = g_gpu_device->GetSyncMode(); - const bool throttle_after_present = (sync_mode == DisplaySyncMode::Disabled); - if (!throttle_after_present && s_throttler_enabled && !IsExecutionInterrupted()) + if (s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted()) Throttle(); const Common::Timer::Value current_time = Common::Timer::GetCurrentValue(); - if (current_time < s_next_frame_time || s_display_all_frames || s_last_frame_skipped) + if (current_time < s_next_frame_time || s_syncing_to_host || s_optimal_frame_pacing || s_last_frame_skipped) { s_last_frame_skipped = !PresentDisplay(true); } @@ -1878,7 +1876,7 @@ void System::FrameDone() s_last_frame_skipped = true; } - if (throttle_after_present && s_throttler_enabled && !IsExecutionInterrupted()) + if (!s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted()) Throttle(); // Input poll already done above @@ -1949,7 +1947,7 @@ void System::Throttle() // Use a spinwait if we undersleep for all platforms except android.. don't want to burn battery. // Linux also seems to do a much better job of waking up at the requested time. #if !defined(__linux__) && !defined(__ANDROID__) - Common::Timer::SleepUntil(s_next_frame_time, g_settings.display_all_frames); + Common::Timer::SleepUntil(s_next_frame_time, g_settings.display_optimal_frame_pacing); #else Common::Timer::SleepUntil(s_next_frame_time, false); #endif @@ -2648,7 +2646,7 @@ void System::UpdateSpeedLimiterState() g_settings.turbo_speed : (s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed); s_throttler_enabled = (s_target_speed != 0.0f); - s_display_all_frames = !s_throttler_enabled || g_settings.display_all_frames; + s_optimal_frame_pacing = s_throttler_enabled && g_settings.display_optimal_frame_pacing; s_syncing_to_host = false; if (g_settings.sync_to_host_refresh_rate && (g_settings.audio_stretch_mode != AudioStretchMode::Off) && @@ -2667,14 +2665,10 @@ void System::UpdateSpeedLimiterState() } // When syncing to host and using vsync, we don't need to sleep. - if (s_syncing_to_host && s_display_all_frames) + if (s_syncing_to_host && IsVSyncEffectivelyEnabled()) { - const DisplaySyncMode effective_sync_mode = GetEffectiveDisplaySyncMode(); - if (effective_sync_mode == DisplaySyncMode::VSync || effective_sync_mode == DisplaySyncMode::VSyncRelaxed) - { - Log_InfoPrintf("Using host vsync for throttling."); - s_throttler_enabled = false; - } + Log_InfoPrintf("Using host vsync for throttling."); + s_throttler_enabled = false; } Log_VerbosePrintf("Target speed: %f%%", s_target_speed * 100.0f); @@ -2707,25 +2701,22 @@ void System::UpdateSpeedLimiterState() void System::UpdateDisplaySync() { - const DisplaySyncMode display_sync_mode = GetEffectiveDisplaySyncMode(); - const bool syncing_to_host_vsync = - (s_syncing_to_host && - (display_sync_mode == DisplaySyncMode::VSync || display_sync_mode == DisplaySyncMode::VSyncRelaxed) && - s_display_all_frames); + const bool vsync_enabled = IsVSyncEffectivelyEnabled(); + const bool syncing_to_host_vsync = (s_syncing_to_host && vsync_enabled); const float max_display_fps = (s_throttler_enabled || s_syncing_to_host) ? 0.0f : g_settings.display_max_fps; - Log_VerbosePrintf("Display sync: %s%s", Settings::GetDisplaySyncModeDisplayName(display_sync_mode), - syncing_to_host_vsync ? " (for throttling)" : ""); - Log_VerbosePrintf("Max display fps: %f (%s)", max_display_fps, - s_display_all_frames ? "displaying all frames" : "skipping displaying frames when needed"); + Log_VerboseFmt("VSync: {}{}", vsync_enabled ? "Enabled" : "Disabled", + syncing_to_host_vsync ? " (for throttling)" : ""); + Log_VerboseFmt("Max display fps: {}", max_display_fps); + Log_VerboseFmt("Preset timing: {}", s_optimal_frame_pacing ? "consistent" : "immediate"); g_gpu_device->SetDisplayMaxFPS(max_display_fps); - g_gpu_device->SetSyncMode(display_sync_mode); + g_gpu_device->SetVSyncEnabled(vsync_enabled); } -DisplaySyncMode System::GetEffectiveDisplaySyncMode() +bool System::IsVSyncEffectivelyEnabled() { // Disable vsync if running outside 100%. - return (IsValid() && IsRunningAtNonStandardSpeed()) ? DisplaySyncMode::Disabled : g_settings.display_sync_mode; + return (g_settings.display_vsync && IsValid() && !IsRunningAtNonStandardSpeed()); } bool System::IsFastForwardEnabled() @@ -3780,12 +3771,12 @@ void System::CheckForSettingsChanges(const Settings& old_settings) DMA::SetHaltTicks(g_settings.dma_halt_ticks); if (g_settings.audio_backend != old_settings.audio_backend || - g_settings.display_sync_mode != old_settings.display_sync_mode || g_settings.increase_timer_resolution != old_settings.increase_timer_resolution || 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.display_all_frames != old_settings.display_all_frames || + g_settings.display_optimal_frame_pacing != old_settings.display_optimal_frame_pacing || + g_settings.display_vsync != old_settings.display_vsync || g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate) { UpdateSpeedLimiterState(); diff --git a/src/core/system.h b/src/core/system.h index 02ee40ab3..5b6d0ee07 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -453,7 +453,7 @@ void ToggleWidescreen(); bool IsRunningAtNonStandardSpeed(); /// Returns true if vsync should be used. -DisplaySyncMode GetEffectiveDisplaySyncMode(); +bool IsVSyncEffectivelyEnabled(); /// Quick switch between software and hardware rendering. void ToggleSoftwareRendering(); diff --git a/src/duckstation-qt/emulationsettingswidget.cpp b/src/duckstation-qt/emulationsettingswidget.cpp index 5db9e7ee0..e1cba8dfa 100644 --- a/src/duckstation-qt/emulationsettingswidget.cpp +++ b/src/duckstation-qt/emulationsettingswidget.cpp @@ -16,8 +16,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget m_ui.setupUi(this); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vsync, "Display", "VSync", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "Main", "SyncToHostRefreshRate", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.displayAllFrames, "Display", "DisplayAllFrames", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.optimalFramePacing, "Display", "OptimalFramePacing", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.rewindEnable, "Main", "RewindEnable", false); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.rewindSaveFrequency, "Main", "RewindFrequency", 10.0f); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.rewindSaveSlots, "Main", "RewindSaveSlots", 10); @@ -67,6 +68,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget } connect(m_ui.turboSpeed, QOverload::of(&QComboBox::currentIndexChanged), this, &EmulationSettingsWidget::onTurboSpeedIndexChanged); + connect(m_ui.vsync, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::onVSyncChanged); connect(m_ui.rewindEnable, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::updateRewind); connect(m_ui.rewindSaveFrequency, QOverload::of(&QDoubleSpinBox::valueChanged), this, @@ -87,6 +89,11 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget m_ui.turboSpeed, tr("Turbo Speed"), tr("User Preference"), tr("Sets the turbo speed. This speed will be used when the turbo hotkey is pressed/toggled. Turboing will take " "priority over fast forwarding if both hotkeys are pressed/toggled.")); + dialog->registerWidgetHelp( + m_ui.vsync, tr("Display Vertical Sync (VSync)"), tr("Unchecked"), + tr("Synchronizes presentation of the console's frames to the host. Enabling may result in smoother animations, at " + "the cost of increased input lag. GSync/FreeSync users should enable Optimal Frame Pacing " + "instead.")); dialog->registerWidgetHelp( m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), tr("Unchecked"), tr("Adjusts the emulation speed so the console's refresh rate matches the host's refresh rate when both VSync and " @@ -94,10 +101,11 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget "potentially increasing the emulation speed by less than 1%. Sync To Host Refresh Rate will not take effect if " "the console's refresh rate is too far from the host's refresh rate. Users with variable refresh rate displays " "should disable this option.")); - dialog->registerWidgetHelp(m_ui.displayAllFrames, tr("Optimal Frame Pacing"), tr("Unchecked"), - tr("Enabling this option will ensure every frame the console renders is displayed to the " - "screen, for optimal frame pacing. If you are having difficulties maintaining full " - "speed, or are getting audio glitches, try disabling this option.")); + dialog->registerWidgetHelp( + m_ui.optimalFramePacing, tr("Optimal Frame Pacing"), tr("Unchecked"), + tr("Enabling this option will ensure every frame the console renders is displayed to the screen, at a consistent " + "rate, for optimal frame pacing. If you have a GSync/FreeSync display, enable this option. If you are having " + "difficulties maintaining full speed, or are getting audio glitches, try disabling this option.")); dialog->registerWidgetHelp( m_ui.rewindEnable, tr("Rewinding"), tr("Unchecked"), tr("Enable Rewinding: Saves state periodically so you can rewind any mistakes while playing.
" @@ -110,6 +118,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget tr( "Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements.")); + onVSyncChanged(); updateRewind(); } @@ -175,6 +184,12 @@ void EmulationSettingsWidget::onTurboSpeedIndexChanged(int index) m_dialog->setFloatSettingValue("Main", "TurboSpeed", okay ? value : 0.0f); } +void EmulationSettingsWidget::onVSyncChanged() +{ + const bool vsync = m_dialog->getEffectiveBoolValue("Display", "VSync", false); + m_ui.syncToHostRefreshRate->setEnabled(vsync); +} + void EmulationSettingsWidget::updateRewind() { const bool rewind_enabled = m_dialog->getEffectiveBoolValue("Main", "RewindEnable", false); diff --git a/src/duckstation-qt/emulationsettingswidget.h b/src/duckstation-qt/emulationsettingswidget.h index 112a7dc29..b66b1d7e9 100644 --- a/src/duckstation-qt/emulationsettingswidget.h +++ b/src/duckstation-qt/emulationsettingswidget.h @@ -21,6 +21,7 @@ private Q_SLOTS: void onEmulationSpeedIndexChanged(int index); void onFastForwardSpeedIndexChanged(int index); void onTurboSpeedIndexChanged(int index); + void onVSyncChanged(); void updateRewind(); private: diff --git a/src/duckstation-qt/emulationsettingswidget.ui b/src/duckstation-qt/emulationsettingswidget.ui index 61974fb18..1f255d096 100644 --- a/src/duckstation-qt/emulationsettingswidget.ui +++ b/src/duckstation-qt/emulationsettingswidget.ui @@ -6,8 +6,8 @@ 0 0 - 672 - 518 + 568 + 369 @@ -61,15 +61,22 @@ - + + + + Display Vertical Sync (VSync) + + + + Sync To Host Refresh Rate - - + + Optimal Frame Pacing @@ -215,7 +222,7 @@ - Qt::Vertical + Qt::Orientation::Vertical diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index c772549cb..7326caf28 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -75,9 +75,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayScaling, "Display", "Scaling", &Settings::ParseDisplayScaling, &Settings::GetDisplayScalingName, Settings::DEFAULT_DISPLAY_SCALING); - SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displaySyncMode, "Display", "SyncMode", - &Settings::ParseDisplaySyncMode, &Settings::GetDisplaySyncModeName, - Settings::DEFAULT_DISPLAY_SYNC_MODE); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gpuDownsampleScale, "GPU", "DownsampleScale", 1); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.trueColor, "GPU", "TrueColor", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableInterlacing, "GPU", "DisableInterlacing", true); @@ -284,10 +281,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* dialog->registerWidgetHelp( m_ui.displayScaling, tr("Scaling"), tr("Bilinear (Smooth)"), tr("Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution.")); - dialog->registerWidgetHelp( - m_ui.displaySyncMode, tr("VSync"), tr("Unchecked"), - tr("Enable this option to match DuckStation's refresh rate with your current monitor or screen. " - "VSync is automatically disabled when it is not possible (e.g. running at non-100% speed).")); dialog->registerWidgetHelp( m_ui.trueColor, tr("True Color Rendering"), tr("Checked"), tr("Forces the precision of colours output to the console's framebuffer to use the full 8 bits of precision per " @@ -550,12 +543,6 @@ void GraphicsSettingsWidget::setupAdditionalUi() QString::fromUtf8(Settings::GetDisplayScalingDisplayName(static_cast(i)))); } - for (u32 i = 0; i < static_cast(DisplaySyncMode::Count); i++) - { - m_ui.displaySyncMode->addItem( - QString::fromUtf8(Settings::GetDisplaySyncModeDisplayName(static_cast(i)))); - } - // Advanced Tab for (u32 i = 0; i < static_cast(DisplayExclusiveFullscreenControl::Count); i++) diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index 8df401b71..ad5b81698 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -7,7 +7,7 @@ 0 0 584 - 450 + 430 @@ -260,6 +260,16 @@ + + + + Deinterlacing: + + + + + + @@ -280,32 +290,8 @@ - - - - VSync: - - - - - - - + - - - - PGXP Geometry Correction - - - - - - - True Color Rendering - - - @@ -313,20 +299,6 @@ - - - - Force 4:3 For FMVs - - - - - - - FMV Chroma Smoothing - - - @@ -334,13 +306,6 @@ - - - - Widescreen Rendering - - - @@ -348,25 +313,50 @@ + + + + Force 4:3 For FMVs + + + + + + + True Color Rendering + + + + + + + FMV Chroma Smoothing + + + + + + + Widescreen Rendering + + + + + + + PGXP Geometry Correction + + + - - - - Deinterlacing: - - - - - - - Qt::Vertical + Qt::Orientation::Vertical @@ -437,20 +427,6 @@ - - - - Threaded Rendering - - - - - - - Threaded Presentation - - - @@ -465,6 +441,20 @@ + + + + Threaded Presentation + + + + + + + Threaded Rendering + + + @@ -488,13 +478,6 @@ - - - - Scaled Dithering - - - @@ -502,6 +485,13 @@ + + + + Scaled Dithering + + + @@ -527,7 +517,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -657,7 +647,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -799,7 +789,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -874,7 +864,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1008,7 +998,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1111,7 +1101,7 @@ - Qt::Vertical + Qt::Orientation::Vertical diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 1ab49a4e7..49f331395 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -92,6 +92,7 @@ static void SetResourcesDirectory(); static bool SetDataDirectory(); static bool SetCriticalFolders(); static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller); +static void MigrateSettings(); static void SaveSettings(); static bool RunSetupWizard(); static std::string GetResourcePath(std::string_view name, bool allow_override); @@ -379,6 +380,7 @@ bool QtHost::InitializeConfig(std::string settings_filename) EmuFolders::LoadConfig(*s_base_settings_interface.get()); EmuFolders::EnsureFoldersExist(); + MigrateSettings(); // We need to create the console window early, otherwise it appears behind the main window. if (!Log::IsConsoleOutputEnabled() && @@ -596,6 +598,21 @@ void QtHost::SetDefaultSettings(SettingsInterface& si, bool system, bool control } } +void QtHost::MigrateSettings() +{ + SmallString value; + if (s_base_settings_interface->GetStringValue("Display", "SyncMode", &value)) + { + s_base_settings_interface->SetBoolValue("Display", "VSync", (value == "VSync" || value == "VSyncRelaxed")); + s_base_settings_interface->SetBoolValue( + "Display", "OptimalFramePacing", + (value == "VRR" || s_base_settings_interface->GetBoolValue("Display", "DisplayAllFrames", false))); + s_base_settings_interface->DeleteValue("Display", "SyncMode"); + s_base_settings_interface->DeleteValue("Display", "DisplayAllFrames"); + s_base_settings_interface->Save(); + } +} + bool EmuThread::shouldRenderToMain() const { return !Host::GetBoolSettingValue("Main", "RenderToSeparateWindow", false) && !QtHost::InNoGUIMode(); @@ -1551,7 +1568,7 @@ void EmuThread::run() if (g_gpu_device) { System::PresentDisplay(false); - if (!g_gpu_device->IsVSyncActive()) + if (!g_gpu_device->IsVSyncEnabled()) g_gpu_device->ThrottlePresentation(); } } diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index a3c639b4e..1e7ee2fbb 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -631,7 +631,7 @@ bool D3D11Device::BeginPresent(bool skip_present) // This blows our our GPU usage number considerably, so read the timestamp before the final blit // in this configuration. It does reduce accuracy a little, but better than seeing 100% all of // the time, when it's more like a couple of percent. - if ((m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed) && m_gpu_timing_enabled) + if (m_vsync_enabled && m_gpu_timing_enabled) PopTimestampQuery(); static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; @@ -648,11 +648,11 @@ void D3D11Device::EndPresent() { DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target); - if (m_sync_mode != DisplaySyncMode::VSync && m_sync_mode != DisplaySyncMode::VSyncRelaxed && m_gpu_timing_enabled) + if (m_vsync_enabled && m_gpu_timing_enabled) PopTimestampQuery(); // DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it. - if (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed) + if (m_vsync_enabled) m_swap_chain->Present(BoolToUInt32(1), 0); else if (m_using_allow_tearing) // Disabled or VRR, VRR requires the allow tearing flag :/ m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index 2ffc1276c..04605e35a 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -1108,7 +1108,7 @@ void D3D12Device::EndPresent() SubmitCommandList(false); // DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it. - if (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed) + if (m_vsync_enabled) m_swap_chain->Present(BoolToUInt32(1), 0); else if (m_using_allow_tearing) // Disabled or VRR, VRR requires the allow tearing flag :/ m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index 2770cbb97..c6750c6b0 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -271,11 +271,10 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs) } bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& shader_cache_path, - u32 shader_cache_version, bool debug_device, DisplaySyncMode sync_mode, - bool threaded_presentation, std::optional exclusive_fullscreen_control, - FeatureMask disabled_features, Error* error) + u32 shader_cache_version, bool debug_device, bool vsync, bool threaded_presentation, + std::optional exclusive_fullscreen_control, FeatureMask disabled_features, Error* error) { - m_sync_mode = sync_mode; + m_vsync_enabled = vsync; m_debug_device = debug_device; if (!AcquireWindow(true)) @@ -586,9 +585,9 @@ void GPUDevice::RenderImGui() } } -void GPUDevice::SetSyncMode(DisplaySyncMode mode) +void GPUDevice::SetVSyncEnabled(bool enabled) { - m_sync_mode = mode; + m_vsync_enabled = enabled; } void GPUDevice::UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex) diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index 495d0099e..a4d2aacf3 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -562,7 +562,7 @@ public: virtual RenderAPI GetRenderAPI() const = 0; bool Create(const std::string_view& adapter, const std::string_view& shader_cache_path, u32 shader_cache_version, - bool debug_device, DisplaySyncMode sync_mode, bool threaded_presentation, + bool debug_device, bool vsync, bool threaded_presentation, std::optional exclusive_fullscreen_control, FeatureMask disabled_features, Error* error); void Destroy(); @@ -660,12 +660,8 @@ public: /// Renders ImGui screen elements. Call before EndPresent(). void RenderImGui(); - ALWAYS_INLINE DisplaySyncMode GetSyncMode() const { return m_sync_mode; } - ALWAYS_INLINE bool IsVSyncActive() const - { - return (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed); - } - virtual void SetSyncMode(DisplaySyncMode mode); + ALWAYS_INLINE bool IsVSyncEnabled() const { return m_vsync_enabled; } + virtual void SetVSyncEnabled(bool enabled); ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; } ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; } @@ -780,7 +776,7 @@ private: protected: static Statistics s_stats; - DisplaySyncMode m_sync_mode = DisplaySyncMode::Disabled; + bool m_vsync_enabled = false; bool m_gpu_timing_enabled = false; bool m_debug_device = false; }; diff --git a/src/util/gpu_types.h b/src/util/gpu_types.h index cf163eec3..988961a62 100644 --- a/src/util/gpu_types.h +++ b/src/util/gpu_types.h @@ -13,12 +13,3 @@ enum class RenderAPI : u32 OpenGLES, Metal }; - -enum class DisplaySyncMode : u8 -{ - Disabled, - VSync, - VSyncRelaxed, - VRR, - Count -}; diff --git a/src/util/metal_device.h b/src/util/metal_device.h index 2f4893abe..8f447db6e 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -265,7 +265,7 @@ public: bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; - void SetSyncMode(DisplaySyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; bool BeginPresent(bool skip_present) override; void EndPresent() override; diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index de0bd1776..b49cd7e2f 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -127,15 +127,14 @@ bool MetalDevice::GetHostRefreshRate(float* refresh_rate) return GPUDevice::GetHostRefreshRate(refresh_rate); } -void MetalDevice::SetSyncMode(DisplaySyncMode mode) +void MetalDevice::SetVSyncEnabled(bool enabled) { - m_sync_mode = mode; + if (m_vsync_enabled == enabled) + return; + m_vsync_enabled = enabled; if (m_layer != nil) - { - const bool enabled = (mode == DisplaySyncMode::VSync || mode == DisplaySyncMode::VSyncRelaxed); [m_layer setDisplaySyncEnabled:enabled]; - } } bool MetalDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation, @@ -389,8 +388,7 @@ bool MetalDevice::CreateLayer() } }); - const bool sync_enabled = (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed); - [m_layer setDisplaySyncEnabled:sync_enabled]; + [m_layer setDisplaySyncEnabled:m_vsync_enabled]; DebugAssert(m_layer_pass_desc == nil); m_layer_pass_desc = [[MTLRenderPassDescriptor renderPassDescriptor] retain]; diff --git a/src/util/opengl_context_wgl.cpp b/src/util/opengl_context_wgl.cpp index acaa18081..dab38c295 100644 --- a/src/util/opengl_context_wgl.cpp +++ b/src/util/opengl_context_wgl.cpp @@ -139,7 +139,7 @@ bool OpenGLContextWGL::SwapBuffers() return ::SwapBuffers(m_dc); } -bool OpenGLContextWGL::IsCurrent() +bool OpenGLContextWGL::IsCurrent() const { return (m_rc && wglGetCurrentContext() == m_rc); } diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index 313386a1d..fe8453972 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -238,12 +238,12 @@ void OpenGLDevice::InsertDebugMessage(const char* msg) #endif } -void OpenGLDevice::SetSyncMode(DisplaySyncMode mode) +void OpenGLDevice::SetVSyncEnabled(bool enabled) { - if (m_sync_mode == mode) + if (m_vsync_enabled == enabled) return; - m_sync_mode = mode; + m_vsync_enabled = enabled; SetSwapInterval(); } @@ -582,14 +582,13 @@ void OpenGLDevice::SetSwapInterval() return; // Window framebuffer has to be bound to call SetSwapInterval. - const s32 interval = - (m_sync_mode == DisplaySyncMode::VSync) ? 1 : ((m_sync_mode == DisplaySyncMode::VSyncRelaxed) ? -1 : 0); + const s32 interval = m_vsync_enabled ? (m_gl_context->SupportsNegativeSwapInterval() ? -1 : 1) : 0; GLint current_fbo = 0; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); if (!m_gl_context->SetSwapInterval(interval)) - Log_WarningPrintf("Failed to set swap interval to %d", interval); + Log_WarningFmt("Failed to set swap interval to {}", interval); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); } @@ -1096,9 +1095,10 @@ void OpenGLDevice::UnmapUniformBuffer(u32 size) glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), pos, size); } -void OpenGLDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, GPUPipeline::RenderPassFlag feedback_loop) +void OpenGLDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, + GPUPipeline::RenderPassFlag feedback_loop) { - //DebugAssert(!feedback_loop); TODO + // DebugAssert(!feedback_loop); TODO bool changed = (m_num_current_render_targets != num_rts || m_current_depth_target != ds); bool needs_ds_clear = (ds && ds->IsClearedOrInvalidated()); bool needs_rt_clear = false; diff --git a/src/util/opengl_device.h b/src/util/opengl_device.h index de56f7fa0..e07c0abfc 100644 --- a/src/util/opengl_device.h +++ b/src/util/opengl_device.h @@ -100,7 +100,7 @@ public: void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override; void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override; - void SetSyncMode(DisplaySyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; bool BeginPresent(bool skip_present) override; void EndPresent() override; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index 272f303a9..17977551f 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -2023,7 +2023,7 @@ bool VulkanDevice::CreateDevice(const std::string_view& adapter, bool threaded_p if (surface != VK_NULL_HANDLE) { - m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_sync_mode, m_exclusive_fullscreen_control); + m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control); if (!m_swap_chain) { Error::SetStringView(error, "Failed to create swap chain"); @@ -2244,7 +2244,7 @@ bool VulkanDevice::UpdateWindow() return false; } - m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_sync_mode, m_exclusive_fullscreen_control); + m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control); if (!m_swap_chain) { Log_ErrorPrintf("Failed to create swap chain"); @@ -2320,22 +2320,21 @@ std::string VulkanDevice::GetDriverInfo() const return ret; } -void VulkanDevice::SetSyncMode(DisplaySyncMode mode) +void VulkanDevice::SetVSyncEnabled(bool enabled) { - if (m_sync_mode == mode) + if (m_vsync_enabled == enabled) return; - const DisplaySyncMode prev_mode = m_sync_mode; - m_sync_mode = mode; + m_vsync_enabled = enabled; if (!m_swap_chain) return; // This swap chain should not be used by the current buffer, thus safe to destroy. WaitForGPUIdle(); - if (!m_swap_chain->SetSyncMode(mode)) + if (!m_swap_chain->SetVSyncEnabled(enabled)) { // Try switching back to the old mode.. - if (!m_swap_chain->SetSyncMode(prev_mode)) + if (!m_swap_chain->SetVSyncEnabled(!enabled)) { Panic("Failed to reset old vsync mode after failure"); m_swap_chain.reset(); diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index 94f7f732e..b681ed48c 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -128,7 +128,7 @@ public: bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; - void SetSyncMode(DisplaySyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; bool BeginPresent(bool skip_present) override; void EndPresent() override; diff --git a/src/util/vulkan_swap_chain.cpp b/src/util/vulkan_swap_chain.cpp index 78af7250e..daf9a46fe 100644 --- a/src/util/vulkan_swap_chain.cpp +++ b/src/util/vulkan_swap_chain.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "vulkan_swap_chain.h" @@ -70,22 +70,10 @@ static const char* PresentModeToString(VkPresentModeKHR mode) } } -static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(DisplaySyncMode mode) -{ - static constexpr std::array(DisplaySyncMode::Count)> modes = {{ - VK_PRESENT_MODE_IMMEDIATE_KHR, // Disabled - VK_PRESENT_MODE_FIFO_KHR, // VSync - VK_PRESENT_MODE_FIFO_RELAXED_KHR, // VSyncRelaxed - VK_PRESENT_MODE_IMMEDIATE_KHR, // VRR ?? - }}; - - return modes[static_cast(mode)]; -} - -VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR requested_present_mode, +VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control) - : m_window_info(wi), m_surface(surface), m_requested_present_mode(requested_present_mode), - m_exclusive_fullscreen_control(exclusive_fullscreen_control) + : m_window_info(wi), m_surface(surface), m_exclusive_fullscreen_control(exclusive_fullscreen_control), + m_vsync_enabled(vsync) { } @@ -220,13 +208,11 @@ void VulkanSwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, #endif } -std::unique_ptr VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, - DisplaySyncMode sync_mode, +std::unique_ptr VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control) { - const VkPresentModeKHR requested_mode = GetPreferredPresentModeForVsyncMode(sync_mode); std::unique_ptr swap_chain = - std::unique_ptr(new VulkanSwapChain(wi, surface, requested_mode, exclusive_fullscreen_control)); + std::unique_ptr(new VulkanSwapChain(wi, surface, vsync, exclusive_fullscreen_control)); if (!swap_chain->CreateSwapChain()) return nullptr; @@ -306,7 +292,7 @@ std::optional VulkanSwapChain::SelectPresentMode(VkSurfaceKHR } else if (requested_mode != VK_PRESENT_MODE_FIFO_KHR && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR)) { - // Prefer mailbox over fifo for adaptive vsync/no-vsync. + // Prefer mailbox over fifo for adaptive vsync/no-vsync. This way it'll only delay one frame. selected_mode = VK_PRESENT_MODE_MAILBOX_KHR; } else if (requested_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR && CheckForMode(VK_PRESENT_MODE_FIFO_KHR)) @@ -333,7 +319,11 @@ bool VulkanSwapChain::CreateSwapChain() // Select swap chain format and present mode std::optional surface_format = SelectSurfaceFormat(m_surface); - std::optional present_mode = SelectPresentMode(m_surface, m_requested_present_mode); + + // Prefer relaxed vsync if available, stalling is bad. + const VkPresentModeKHR requested_mode = + m_vsync_enabled ? VK_PRESENT_MODE_FIFO_RELAXED_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR; + std::optional present_mode = SelectPresentMode(m_surface, requested_mode); if (!surface_format.has_value() || !present_mode.has_value()) return false; @@ -643,13 +633,12 @@ bool VulkanSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_s return true; } -bool VulkanSwapChain::SetSyncMode(DisplaySyncMode mode) +bool VulkanSwapChain::SetVSyncEnabled(bool enabled) { - const VkPresentModeKHR present_mode = GetPreferredPresentModeForVsyncMode(mode); - if (m_requested_present_mode == present_mode) + if (m_vsync_enabled == enabled) return true; - m_requested_present_mode = present_mode; + m_vsync_enabled = enabled; // Recreate the swap chain with the new present mode. Log_VerbosePrintf("Recreating swap chain to change present mode."); diff --git a/src/util/vulkan_swap_chain.h b/src/util/vulkan_swap_chain.h index 7229e216c..3f126eb29 100644 --- a/src/util/vulkan_swap_chain.h +++ b/src/util/vulkan_swap_chain.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once @@ -25,7 +25,7 @@ public: static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface); // Create a new swap chain from a pre-existing surface. - static std::unique_ptr Create(const WindowInfo& wi, VkSurfaceKHR surface, DisplaySyncMode sync_mode, + static std::unique_ptr Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control); ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; } @@ -73,10 +73,10 @@ public: bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f); // Change vsync enabled state. This may fail as it causes a swapchain recreation. - bool SetSyncMode(DisplaySyncMode mode); + bool SetVSyncEnabled(bool enabled); private: - VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR requested_present_mode, + VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control); static std::optional SelectSurfaceFormat(VkSurfaceKHR surface); @@ -111,11 +111,11 @@ private: std::vector m_semaphores; VkFormat m_format = VK_FORMAT_UNDEFINED; - VkPresentModeKHR m_requested_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; VkPresentModeKHR m_actual_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; u32 m_current_image = 0; u32 m_current_semaphore = 0; std::optional m_image_acquire_result; std::optional m_exclusive_fullscreen_control; + bool m_vsync_enabled = false; };