diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index cbe4f1e78c..4444e6cde3 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -107,7 +107,7 @@ bool GSRunner::InitializeConfig() // complete as quickly as possible si.SetBoolValue("EmuCore/GS", "FrameLimitEnable", false); - si.SetIntValue("EmuCore/GS", "VsyncEnable", static_cast(VsyncMode::Off)); + si.SetIntValue("EmuCore/GS", "VsyncEnable", false); // ensure all input sources are disabled, we're not using them si.SetBoolValue("InputSources", "SDL", false); diff --git a/pcsx2/Config.h b/pcsx2/Config.h index c8e1a4a333..12c4589730 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -190,13 +190,6 @@ enum class SpeedHack MaxCount, }; -enum class VsyncMode -{ - Off, - On, - Adaptive, -}; - enum class AspectRatioType : u8 { Stretch, @@ -596,6 +589,8 @@ struct Pcsx2Config struct { bool + SynchronousMTGS : 1, + VsyncEnable : 1, PCRTCAntiBlur : 1, DisableInterlaceOffset : 1, PCRTCOffsets : 1, @@ -618,9 +613,7 @@ struct Pcsx2Config OsdShowIndicators : 1, OsdShowSettings : 1, OsdShowInputs : 1, - OsdShowFrameTimes : 1; - - bool + OsdShowFrameTimes : 1, HWSpinGPUForReadbacks : 1, HWSpinCPUForReadbacks : 1, GPUPaletteConversion : 1, @@ -664,12 +657,6 @@ struct Pcsx2Config int VsyncQueueSize = 2; - // forces the MTGS to execute tags/tasks in fully blocking/synchronous - // style. Useful for debugging potential bugs in the MTGS pipeline. - bool SynchronousMTGS = false; - - VsyncMode VsyncEnable = VsyncMode::Off; - float FramerateNTSC = DEFAULT_FRAME_RATE_NTSC; float FrameratePAL = DEFAULT_FRAME_RATE_PAL; diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 9fa2c6f882..4b2f25e368 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -471,7 +471,7 @@ void GSPresentCurrentFrame() void GSThrottlePresentation() { - if (g_gs_device->GetVsyncMode() != VsyncMode::Off) + if (g_gs_device->IsVSyncEnabled()) { // Let vsync take care of throttling. return; @@ -528,9 +528,9 @@ void GSUpdateDisplayWindow() ImGuiManager::WindowResized(); } -void GSSetVSyncMode(VsyncMode mode) +void GSSetVSyncEnabled(bool enabled) { - g_gs_device->SetVSync(mode); + g_gs_device->SetVSyncEnabled(enabled); } bool GSWantsExclusiveFullscreen() diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index 4b42cd32ef..efd1fdc03b 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -84,7 +84,7 @@ void GSSetDisplayAlignment(GSDisplayAlignment alignment); bool GSHasDisplayWindow(); void GSResizeDisplayWindow(int width, int height, float scale); void GSUpdateDisplayWindow(); -void GSSetVSyncMode(VsyncMode mode); +void GSSetVSyncEnabled(bool enabled); GSRendererType GSGetCurrentRenderer(); bool GSIsHardwareRenderer(); @@ -127,7 +127,7 @@ namespace Host void SetFullscreen(bool enabled); /// Returns the desired vsync mode, depending on the runtime environment. - VsyncMode GetEffectiveVSyncMode(); + bool IsVsyncEffectivelyEnabled(); /// Called when video capture starts or stops. Called on the MTGS thread. void OnCaptureStarted(const std::string& filename); diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index d0b40b11d2..7e49535fe2 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -307,7 +307,7 @@ int GSDevice::GetMipmapLevelsForSize(int width, int height) bool GSDevice::Create() { - m_vsync_mode = Host::GetEffectiveVSyncMode(); + m_vsync_enabled = Host::IsVsyncEffectivelyEnabled(); return true; } diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index c990f1683c..3003f079e2 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -803,7 +803,6 @@ protected: static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u16) * 16383 * 6; WindowInfo m_window_info; - VsyncMode m_vsync_mode = VsyncMode::Off; GSTexture* m_imgui_font = nullptr; @@ -824,6 +823,7 @@ protected: u32 start, count; } m_index = {}; unsigned int m_frame = 0; // for ageing the pool + bool m_vsync_enabled = false; bool m_rbswapped = false; FeatureSupport m_features; @@ -874,7 +874,7 @@ public: __fi s32 GetWindowHeight() const { return static_cast(m_window_info.surface_height); } __fi GSVector2i GetWindowSize() const { return GSVector2i(static_cast(m_window_info.surface_width), static_cast(m_window_info.surface_height)); } __fi float GetWindowScale() const { return m_window_info.surface_scale; } - __fi VsyncMode GetVsyncMode() const { return m_vsync_mode; } + __fi bool IsVSyncEnabled() const { return m_vsync_enabled; } __fi GSTexture* GetCurrent() const { return m_current; } @@ -915,7 +915,7 @@ public: virtual void EndPresent() = 0; /// Changes vsync mode for this display. - virtual void SetVSync(VsyncMode mode) = 0; + virtual void SetVSyncEnabled(bool enabled) = 0; /// Returns the effective refresh rate of this display. virtual bool GetHostRefreshRate(float* refresh_rate); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index fa55574974..d4251a714f 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -628,9 +628,9 @@ bool GSDevice11::GetHostRefreshRate(float* refresh_rate) return GSDevice::GetHostRefreshRate(refresh_rate); } -void GSDevice11::SetVSync(VsyncMode mode) +void GSDevice11::SetVSyncEnabled(bool enabled) { - m_vsync_mode = mode; + m_vsync_enabled = enabled; } bool GSDevice11::CreateSwapChain() @@ -928,7 +928,7 @@ GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip) // 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_vsync_mode != VsyncMode::Off && m_gpu_timing_enabled) + if (m_vsync_enabled && m_gpu_timing_enabled) PopTimestampQuery(); m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), s_present_clear_color.data()); @@ -957,14 +957,13 @@ void GSDevice11::EndPresent() RenderImGui(); // See note in BeginPresent() for why it's conditional on vsync-off. - const bool vsync_on = m_vsync_mode != VsyncMode::Off; - if (!vsync_on && m_gpu_timing_enabled) + if (!m_vsync_enabled && m_gpu_timing_enabled) PopTimestampQuery(); - if (!vsync_on && m_using_allow_tearing) + if (!m_vsync_enabled && m_using_allow_tearing) m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); else - m_swap_chain->Present(static_cast(vsync_on), 0); + m_swap_chain->Present(static_cast(m_vsync_enabled), 0); if (m_gpu_timing_enabled) KickTimestampQuery(); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index 8a7777a3c2..d989981972 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -273,7 +273,7 @@ public: bool GetHostRefreshRate(float* refresh_rate) override; - void SetVSync(VsyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; PresentResult BeginPresent(bool frame_skip) override; void EndPresent() override; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 9294bfb0b4..28339275aa 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -774,9 +774,9 @@ bool GSDevice12::GetHostRefreshRate(float* refresh_rate) return GSDevice::GetHostRefreshRate(refresh_rate); } -void GSDevice12::SetVSync(VsyncMode mode) +void GSDevice12::SetVSyncEnabled(bool enabled) { - m_vsync_mode = mode; + m_vsync_enabled = enabled; } bool GSDevice12::CreateSwapChain() @@ -1109,11 +1109,10 @@ void GSDevice12::EndPresent() return; } - const bool vsync = static_cast(m_vsync_mode != VsyncMode::Off); - if (!vsync && m_using_allow_tearing) + if (!m_vsync_enabled && m_using_allow_tearing) m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); else - m_swap_chain->Present(static_cast(vsync), 0); + m_swap_chain->Present(static_cast(m_vsync_enabled), 0); InvalidateCachedState(); } diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.h b/pcsx2/GS/Renderers/DX12/GSDevice12.h index 49abdf47b0..d04e6c51ab 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.h +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.h @@ -409,7 +409,7 @@ public: bool GetHostRefreshRate(float* refresh_rate) override; - void SetVSync(VsyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; PresentResult BeginPresent(bool frame_skip) override; void EndPresent() override; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h index f99cd9b7c1..53b5805cce 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h @@ -392,7 +392,7 @@ public: PresentResult BeginPresent(bool frame_skip) override; void EndPresent() override; - void SetVSync(VsyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; bool GetHostRefreshRate(float* refresh_rate) override; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index b9240682c1..fc6d00d250 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -877,7 +877,7 @@ bool GSDeviceMTL::Create() { AttachSurfaceOnMainThread(); }); - [m_layer setDisplaySyncEnabled:m_vsync_mode != VsyncMode::Off]; + [m_layer setDisplaySyncEnabled:m_vsync_enabled]; } else { @@ -1305,7 +1305,7 @@ void GSDeviceMTL::EndPresent() if (m_current_drawable) { const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always || - (m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_mode != VsyncMode::Off); + (m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_enabled); if (use_present_drawable) [m_current_render_cmdbuf presentDrawable:m_current_drawable]; @@ -1367,13 +1367,13 @@ void GSDeviceMTL::EndPresent() } }} -void GSDeviceMTL::SetVSync(VsyncMode mode) +void GSDeviceMTL::SetVSyncEnabled(bool enabled) { - if (m_vsync_mode == mode) + if (m_vsync_enabled == enabled) return; - [m_layer setDisplaySyncEnabled:mode != VsyncMode::Off]; - m_vsync_mode = mode; + [m_layer setDisplaySyncEnabled:enabled]; + m_vsync_enabled = enabled; } bool GSDeviceMTL::GetHostRefreshRate(float* refresh_rate) diff --git a/pcsx2/GS/Renderers/OpenGL/GLContext.h b/pcsx2/GS/Renderers/OpenGL/GLContext.h index cef1458e37..b75e7217d0 100644 --- a/pcsx2/GS/Renderers/OpenGL/GLContext.h +++ b/pcsx2/GS/Renderers/OpenGL/GLContext.h @@ -35,6 +35,7 @@ public: virtual bool IsCurrent() = 0; virtual bool MakeCurrent() = 0; virtual bool DoneCurrent() = 0; + virtual bool SupportsNegativeSwapInterval() const = 0; virtual bool SetSwapInterval(s32 interval) = 0; virtual std::unique_ptr CreateSharedContext(const WindowInfo& wi, Error* error) = 0; diff --git a/pcsx2/GS/Renderers/OpenGL/GLContextEGL.cpp b/pcsx2/GS/Renderers/OpenGL/GLContextEGL.cpp index 69f5948364..b905f02856 100644 --- a/pcsx2/GS/Renderers/OpenGL/GLContextEGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GLContextEGL.cpp @@ -312,6 +312,11 @@ bool GLContextEGL::DoneCurrent() return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } +bool GLContextEGL::SupportsNegativeSwapInterval() const +{ + return m_supports_negative_swap_interval; +} + bool GLContextEGL::SetSwapInterval(s32 interval) { return eglSwapInterval(m_display, interval); @@ -492,6 +497,19 @@ bool GLContextEGL::CreateContext(const Version& version, EGLContext share_contex } Console.WriteLnFmt("Got GL version {}.{}", version.major_version, version.minor_version); + + EGLint min_swap_interval, max_swap_interval; + m_supports_negative_swap_interval = false; + if (eglGetConfigAttrib(m_display, config.value(), EGL_MIN_SWAP_INTERVAL, &min_swap_interval) && + eglGetConfigAttrib(m_display, config.value(), EGL_MAX_SWAP_INTERVAL, &max_swap_interval)) + { + DEV_LOG("EGL_MIN_SWAP_INTERVAL = {}", min_swap_interval); + DEV_LOG("EGL_MAX_SWAP_INTERVAL = {}", max_swap_interval); + m_supports_negative_swap_interval = (min_swap_interval <= -1); + } + + INFO_LOG("Negative swap interval/tear-control is {}supported", m_supports_negative_swap_interval ? "" : "NOT "); + m_config = config.value(); m_version = version; return true; diff --git a/pcsx2/GS/Renderers/OpenGL/GLContextEGL.h b/pcsx2/GS/Renderers/OpenGL/GLContextEGL.h index 23ef99eb9c..52eeb571eb 100644 --- a/pcsx2/GS/Renderers/OpenGL/GLContextEGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GLContextEGL.h @@ -24,6 +24,7 @@ public: bool IsCurrent() override; bool MakeCurrent() override; bool DoneCurrent() override; + bool SupportsNegativeSwapInterval() const override; bool SetSwapInterval(s32 interval) override; virtual std::unique_ptr CreateSharedContext(const WindowInfo& wi, Error* error) override; @@ -52,4 +53,5 @@ protected: EGLConfig m_config = {}; bool m_use_ext_platform_base = false; + bool m_supports_negative_swap_interval = false; }; diff --git a/pcsx2/GS/Renderers/OpenGL/GLContextWGL.cpp b/pcsx2/GS/Renderers/OpenGL/GLContextWGL.cpp index 0ba43519a4..6a75e3885c 100644 --- a/pcsx2/GS/Renderers/OpenGL/GLContextWGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GLContextWGL.cpp @@ -152,6 +152,11 @@ bool GLContextWGL::DoneCurrent() return wglMakeCurrent(m_dc, nullptr); } +bool GLContextWGL::SupportsNegativeSwapInterval() const +{ + return GLAD_WGL_EXT_swap_control && GLAD_WGL_EXT_swap_control_tear; +} + bool GLContextWGL::SetSwapInterval(s32 interval) { if (!GLAD_WGL_EXT_swap_control) diff --git a/pcsx2/GS/Renderers/OpenGL/GLContextWGL.h b/pcsx2/GS/Renderers/OpenGL/GLContextWGL.h index bea67f891b..aa19ef551b 100644 --- a/pcsx2/GS/Renderers/OpenGL/GLContextWGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GLContextWGL.h @@ -27,6 +27,7 @@ public: bool IsCurrent() override; bool MakeCurrent() override; bool DoneCurrent() override; + bool SupportsNegativeSwapInterval() const override; bool SetSwapInterval(s32 interval) override; std::unique_ptr CreateSharedContext(const WindowInfo& wi, Error* error) override; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 43ec942a60..a4cb000372 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -146,21 +146,13 @@ bool GSDeviceOGL::HasSurface() const return m_window_info.type != WindowInfo::Type::Surfaceless; } -void GSDeviceOGL::SetVSync(VsyncMode mode) +void GSDeviceOGL::SetVSyncEnabled(bool enabled) { - if (m_vsync_mode == mode || m_gl_context->GetWindowInfo().type == WindowInfo::Type::Surfaceless) + if (m_vsync_enabled == enabled) return; - // Window framebuffer has to be bound to call SetSwapInterval. - GLint current_fbo = 0; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - - if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1)) - m_gl_context->SetSwapInterval(static_cast(mode != VsyncMode::Off)); - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); - m_vsync_mode = mode; + m_vsync_enabled = enabled; + SetSwapInterval(); } bool GSDeviceOGL::Create() @@ -777,8 +769,19 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo) void GSDeviceOGL::SetSwapInterval() { - const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0)); - m_gl_context->SetSwapInterval(interval); + if (m_window_info.type == WindowInfo::Type::Surfaceless) + return; + + // Window framebuffer has to be bound to call SetSwapInterval. + 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)) + WARNING_LOG("Failed to set swap interval to {}", interval); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); } void GSDeviceOGL::DestroyResources() @@ -868,9 +871,7 @@ bool GSDeviceOGL::UpdateWindow() if (m_window_info.type != WindowInfo::Type::Surfaceless) { // reset vsync rate, since it (usually) gets lost - if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1)) - m_gl_context->SetSwapInterval(static_cast(m_vsync_mode != VsyncMode::Off)); - + SetSwapInterval(); RenderBlankFrame(); } diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h index f49a8b98e8..a30de2d1cf 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h @@ -292,7 +292,7 @@ public: void DestroySurface() override; std::string GetDriverInfo() const override; - void SetVSync(VsyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; PresentResult BeginPresent(bool frame_skip) override; void EndPresent() override; diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 2f523fc189..676fabb06f 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -2222,7 +2222,7 @@ bool GSDeviceVK::UpdateWindow() return false; } - m_swap_chain = VKSwapChain::Create(m_window_info, surface, m_vsync_mode, + m_swap_chain = VKSwapChain::Create(m_window_info, surface, m_vsync_enabled, Pcsx2Config::GSOptions::TriStateToOptionalBoolean(GSConfig.ExclusiveFullscreenControl)); if (!m_swap_chain) { @@ -2297,24 +2297,27 @@ std::string GSDeviceVK::GetDriverInfo() const return ret; } -void GSDeviceVK::SetVSync(VsyncMode mode) +void GSDeviceVK::SetVSyncEnabled(bool enabled) { - if (!m_swap_chain || m_vsync_mode == mode) + if (!m_swap_chain || m_vsync_enabled == enabled) + { + m_vsync_enabled = enabled; return; + } // This swap chain should not be used by the current buffer, thus safe to destroy. WaitForGPUIdle(); - if (!m_swap_chain->SetVSync(mode)) + if (!m_swap_chain->SetVSyncEnabled(enabled)) { // Try switching back to the old mode.. - if (!m_swap_chain->SetVSync(m_vsync_mode)) + if (!m_swap_chain->SetVSyncEnabled(m_vsync_enabled)) { pxFailRel("Failed to reset old vsync mode after failure"); m_swap_chain.reset(); } } - m_vsync_mode = mode; + m_vsync_enabled = enabled; } GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip) @@ -2613,7 +2616,7 @@ bool GSDeviceVK::CreateDeviceAndSwapChain() if (surface != VK_NULL_HANDLE) { - m_swap_chain = VKSwapChain::Create(m_window_info, surface, m_vsync_mode, + m_swap_chain = VKSwapChain::Create(m_window_info, surface, m_vsync_enabled, Pcsx2Config::GSOptions::TriStateToOptionalBoolean(GSConfig.ExclusiveFullscreenControl)); if (!m_swap_chain) { diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h index 6fff081482..94f82157bd 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h @@ -524,7 +524,7 @@ public: void DestroySurface() override; std::string GetDriverInfo() const override; - void SetVSync(VsyncMode mode) override; + void SetVSyncEnabled(bool enabled) override; PresentResult BeginPresent(bool frame_skip) override; void EndPresent() override; diff --git a/pcsx2/GS/Renderers/Vulkan/VKSwapChain.cpp b/pcsx2/GS/Renderers/Vulkan/VKSwapChain.cpp index f0f3f05426..fc296ba20b 100644 --- a/pcsx2/GS/Renderers/Vulkan/VKSwapChain.cpp +++ b/pcsx2/GS/Renderers/Vulkan/VKSwapChain.cpp @@ -18,11 +18,11 @@ #endif VKSwapChain::VKSwapChain( - const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional exclusive_fullscreen_control) + const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control) : m_window_info(wi) , m_surface(surface) - , m_vsync_mode(vsync) , m_exclusive_fullscreen_control(exclusive_fullscreen_control) + , m_vsync_enabled(vsync) { } @@ -135,7 +135,7 @@ void VKSwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSu } std::unique_ptr VKSwapChain::Create( - const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional exclusive_fullscreen_control) + const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control) { std::unique_ptr swap_chain = std::unique_ptr(new VKSwapChain(wi, surface, vsync, exclusive_fullscreen_control)); @@ -226,17 +226,7 @@ static const char* PresentModeToString(VkPresentModeKHR mode) } } -static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(VsyncMode mode) -{ - if (mode == VsyncMode::On) - return VK_PRESENT_MODE_FIFO_KHR; - else if (mode == VsyncMode::Adaptive) - return VK_PRESENT_MODE_FIFO_RELAXED_KHR; - else - return VK_PRESENT_MODE_IMMEDIATE_KHR; -} - -std::optional VKSwapChain::SelectPresentMode(VkSurfaceKHR surface, VsyncMode vsync) +std::optional VKSwapChain::SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_mode) { VkResult res; u32 mode_count; @@ -260,31 +250,30 @@ std::optional VKSwapChain::SelectPresentMode(VkSurfaceKHR surf return it != present_modes.end(); }; - // Use preferred mode if available. - const VkPresentModeKHR preferred_mode = GetPreferredPresentModeForVsyncMode(vsync); - VkPresentModeKHR selected_mode; - if (CheckForMode(preferred_mode)) - { - selected_mode = preferred_mode; - } - else if (vsync != VsyncMode::On && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR)) - { - // Prefer mailbox over fifo for adaptive vsync/no-vsync. - selected_mode = VK_PRESENT_MODE_MAILBOX_KHR; - } - else if (vsync != VsyncMode::Off && CheckForMode(VK_PRESENT_MODE_FIFO_KHR)) - { - // Fallback to FIFO if we're using any kind of vsync. - // This should never fail, FIFO is mandated. - selected_mode = VK_PRESENT_MODE_FIFO_KHR; - } - else - { - // Fall back to whatever is available. - selected_mode = present_modes[0]; - } + // Use preferred mode if available. + VkPresentModeKHR selected_mode; + if (CheckForMode(requested_mode)) + { + selected_mode = requested_mode; + } + else if (requested_mode != VK_PRESENT_MODE_FIFO_KHR && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR)) + { + // 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)) + { + // Fallback to FIFO if we're using any kind of vsync. + // This should never fail, FIFO is mandated. + selected_mode = VK_PRESENT_MODE_FIFO_KHR; + } + else + { + // Fall back to whatever is available. + selected_mode = present_modes[0]; + } - DevCon.WriteLn("(SwapChain) Preferred present mode: %s, selected: %s", PresentModeToString(preferred_mode), + DevCon.WriteLn("(SwapChain) Preferred present mode: %s, selected: %s", PresentModeToString(requested_mode), PresentModeToString(selected_mode)); return selected_mode; @@ -294,7 +283,11 @@ bool VKSwapChain::CreateSwapChain() { // Select swap chain format and present mode std::optional surface_format = SelectSurfaceFormat(m_surface); - std::optional present_mode = SelectPresentMode(m_surface, m_vsync_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; @@ -414,6 +407,7 @@ bool VKSwapChain::CreateSwapChain() m_window_info.surface_width = std::max(1u, size.width); m_window_info.surface_height = std::max(1u, size.height); + m_actual_present_mode = present_mode.value(); // Get and create images. pxAssert(m_images.empty()); @@ -558,12 +552,12 @@ bool VKSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_scale return true; } -bool VKSwapChain::SetVSync(VsyncMode mode) +bool VKSwapChain::SetVSyncEnabled(bool enabled) { - if (m_vsync_mode == mode) + if (m_vsync_enabled == enabled) return true; - m_vsync_mode = mode; + m_vsync_enabled = enabled; // Recreate the swap chain with the new present mode. DevCon.WriteLn("Recreating swap chain to change present mode."); diff --git a/pcsx2/GS/Renderers/Vulkan/VKSwapChain.h b/pcsx2/GS/Renderers/Vulkan/VKSwapChain.h index b359e879cb..5a1fdfbe5d 100644 --- a/pcsx2/GS/Renderers/Vulkan/VKSwapChain.h +++ b/pcsx2/GS/Renderers/Vulkan/VKSwapChain.h @@ -25,7 +25,7 @@ public: // Create a new swap chain from a pre-existing surface. static std::unique_ptr Create( - const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional exclusive_fullscreen_control); + const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control); __fi VkSurfaceKHR GetSurface() const { return m_surface; } __fi VkSwapchainKHR GetSwapChain() const { return m_swap_chain; } @@ -57,7 +57,11 @@ public: } // Returns true if the current present mode is synchronizing (adaptive or hard). - __fi bool IsPresentModeSynchronizing() const { return (m_vsync_mode != VsyncMode::Off); } + __fi bool IsPresentModeSynchronizing() const + { + return (m_actual_present_mode == VK_PRESENT_MODE_FIFO_KHR || + m_actual_present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR); + } VkFormat GetTextureFormat() const; VkResult AcquireNextImage(); @@ -67,14 +71,14 @@ 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 SetVSync(VsyncMode mode); + bool SetVSyncEnabled(bool enabled); private: VKSwapChain( - const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional exclusive_fullscreen_control); + const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional exclusive_fullscreen_control); static std::optional SelectSurfaceFormat(VkSurfaceKHR surface); - static std::optional SelectPresentMode(VkSurfaceKHR surface, VsyncMode vsync); + static std::optional SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_mode); bool CreateSwapChain(); void DestroySwapChain(); @@ -98,10 +102,11 @@ private: std::vector> m_images; std::vector m_semaphores; - VsyncMode m_vsync_mode = VsyncMode::Off; + 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; }; diff --git a/pcsx2/Host.h b/pcsx2/Host.h index 847cb9548e..0eb469d54d 100644 --- a/pcsx2/Host.h +++ b/pcsx2/Host.h @@ -16,8 +16,6 @@ #include #include -enum class VsyncMode; - class SettingsInterface; namespace Host diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index f876ed2b64..a03367aad9 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -3482,11 +3482,6 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad "13", //GSRendererType::SW, "11", //GSRendererType::Null }; - static constexpr const char* s_vsync_values[] = { - FSUI_NSTR("Off"), - FSUI_NSTR("On"), - FSUI_NSTR("Adaptive"), - }; static constexpr const char* s_bilinear_present_options[] = { FSUI_NSTR("Off"), FSUI_NSTR("Bilinear (Smooth)"), @@ -3621,8 +3616,8 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad MenuHeading(FSUI_CSTR("Renderer")); DrawStringListSetting(bsi, FSUI_CSTR("Renderer"), FSUI_CSTR("Selects the API used to render the emulated GS."), "EmuCore/GS", "Renderer", "-1", s_renderer_names, s_renderer_values, std::size(s_renderer_names), true); - DrawIntListSetting(bsi, FSUI_CSTR("Sync To Host Refresh (VSync)"), FSUI_CSTR("Synchronizes frame presentation with host refresh."), - "EmuCore/GS", "VsyncEnable", static_cast(VsyncMode::Off), s_vsync_values, std::size(s_vsync_values), true); + DrawToggleSetting(bsi, FSUI_CSTR("Sync To Host Refresh (VSync)"), FSUI_CSTR("Synchronizes frame presentation with host refresh."), + "EmuCore/GS", "VsyncEnable", false); MenuHeading(FSUI_CSTR("Display")); DrawStringListSetting(bsi, FSUI_CSTR("Aspect Ratio"), FSUI_CSTR("Selects the aspect ratio to display the game content at."), @@ -7410,8 +7405,6 @@ TRANSLATE_NOOP("FullscreenUI", "Metal"); TRANSLATE_NOOP("FullscreenUI", "Software"); TRANSLATE_NOOP("FullscreenUI", "Null"); TRANSLATE_NOOP("FullscreenUI", "Off"); -TRANSLATE_NOOP("FullscreenUI", "On"); -TRANSLATE_NOOP("FullscreenUI", "Adaptive"); TRANSLATE_NOOP("FullscreenUI", "Bilinear (Smooth)"); TRANSLATE_NOOP("FullscreenUI", "Bilinear (Sharp)"); TRANSLATE_NOOP("FullscreenUI", "No Deinterlacing"); diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 757025af86..b7579b9e86 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -931,7 +931,7 @@ void MTGS::ApplySettings() RunOnGSThread([opts = EmuConfig.GS]() { GSUpdateConfig(opts); - GSSetVSyncMode(Host::GetEffectiveVSyncMode()); + GSSetVSyncEnabled(Host::IsVsyncEffectivelyEnabled()); }); // We need to synchronize the thread when changing any settings when the download mode @@ -970,19 +970,19 @@ void MTGS::UpdateDisplayWindow() }); } -void MTGS::SetVSyncMode(VsyncMode mode) +void MTGS::SetVSyncEnabled(bool enabled) { pxAssertRel(IsOpen(), "MTGS is running"); - RunOnGSThread([mode]() { - Console.WriteLn("Vsync is %s", mode == VsyncMode::Off ? "OFF" : (mode == VsyncMode::Adaptive ? "ADAPTIVE" : "ON")); - GSSetVSyncMode(mode); + RunOnGSThread([enabled]() { + INFO_LOG("Vsync is {}", enabled ? "ON" : "OFF"); + GSSetVSyncEnabled(enabled); }); } -void MTGS::UpdateVSyncMode() +void MTGS::UpdateVSyncEnabled() { - SetVSyncMode(Host::GetEffectiveVSyncMode()); + SetVSyncEnabled(Host::IsVsyncEffectivelyEnabled()); } void MTGS::SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message /* = true */) diff --git a/pcsx2/MTGS.h b/pcsx2/MTGS.h index 6f2cb77773..0921f8e310 100644 --- a/pcsx2/MTGS.h +++ b/pcsx2/MTGS.h @@ -70,8 +70,8 @@ namespace MTGS void ApplySettings(); void ResizeDisplayWindow(int width, int height, float scale); void UpdateDisplayWindow(); - void SetVSyncMode(VsyncMode mode); - void UpdateVSyncMode(); + void SetVSyncEnabled(bool enabled); + void UpdateVSyncEnabled(); void SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message = true); void ToggleSoftwareRendering(); bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 5a89e24009..86b6ae3a1c 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -793,11 +793,12 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap) SettingsWrapSection("EmuCore/GS"); #ifdef PCSX2_DEVBUILD - SettingsWrapEntry(SynchronousMTGS); + SettingsWrapBitBool(SynchronousMTGS); #endif - SettingsWrapEntry(VsyncQueueSize); - wrap.EnumEntry(CURRENT_SETTINGS_SECTION, "VsyncEnable", VsyncEnable, NULL, VsyncEnable); + SettingsWrapBitBool(VsyncEnable); + + SettingsWrapEntry(VsyncQueueSize); SettingsWrapEntry(FramerateNTSC); SettingsWrapEntry(FrameratePAL); diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index e3bde50353..88c8550121 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -2004,7 +2004,7 @@ double VMManager::AdjustToHostRefreshRate(float frame_rate, float target_speed) const float ratio = host_refresh_rate / frame_rate; const bool syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f); s_target_speed_synced_to_host = syncing_to_host; - s_use_vsync_for_timing = (syncing_to_host && !EmuConfig.GS.SkipDuplicateFrames && EmuConfig.GS.VsyncEnable != VsyncMode::Off); + s_use_vsync_for_timing = (syncing_to_host && !EmuConfig.GS.SkipDuplicateFrames && EmuConfig.GS.VsyncEnable); Console.WriteLn("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s %s", host_refresh_rate, frame_rate, ratio, syncing_to_host ? "can sync" : "can't sync", s_use_vsync_for_timing ? "and using vsync for pacing" : "and using sleep for pacing"); @@ -2051,7 +2051,7 @@ void VMManager::UpdateTargetSpeed() { s_target_speed = target_speed; - MTGS::UpdateVSyncMode(); + MTGS::UpdateVSyncEnabled(); SPU2::OnTargetSpeedChanged(); ResetFrameLimiter(); } @@ -2546,13 +2546,13 @@ void VMManager::SetPaused(bool paused) SetState(paused ? VMState::Paused : VMState::Running); } -VsyncMode Host::GetEffectiveVSyncMode() +bool Host::IsVsyncEffectivelyEnabled() { const bool has_vm = VMManager::GetState() != VMState::Shutdown; // Force vsync off when not running at 100% speed. if (has_vm && (s_target_speed != 1.0f && !s_use_vsync_for_timing)) - return VsyncMode::Off; + return false; // Otherwise use the config setting. return EmuConfig.GS.VsyncEnable; @@ -2739,6 +2739,7 @@ void VMManager::CheckForGSConfigChanges(const Pcsx2Config& old_config) { // Still need to update target speed, because of sync-to-host-refresh. UpdateTargetSpeed(); + MTGS::UpdateVSyncEnabled(); } MTGS::ApplySettings();