diff --git a/common/Vulkan/Context.cpp b/common/Vulkan/Context.cpp index 51cf6bb607..bbe0d43d66 100644 --- a/common/Vulkan/Context.cpp +++ b/common/Vulkan/Context.cpp @@ -291,7 +291,7 @@ namespace Vulkan } bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::unique_ptr* out_swap_chain, - bool threaded_presentation, bool enable_debug_utils, bool enable_validation_layer) + bool vsync, bool threaded_presentation, bool enable_debug_utils, bool enable_validation_layer) { pxAssertMsg(!g_vulkan_context, "Has no current context"); @@ -370,7 +370,7 @@ namespace Vulkan if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer, nullptr, 0, nullptr, 0, nullptr) || !g_vulkan_context->CreateAllocator() || !g_vulkan_context->CreateGlobalDescriptorPool() || !g_vulkan_context->CreateCommandBuffers() || !g_vulkan_context->CreateTextureStreamBuffer() || - (enable_surface && (*out_swap_chain = SwapChain::Create(wi_copy, surface, true)) == nullptr)) + (enable_surface && (*out_swap_chain = SwapChain::Create(wi_copy, surface, vsync)) == nullptr)) { // Since we are destroying the instance, we're also responsible for destroying the surface. if (surface != VK_NULL_HANDLE) diff --git a/common/Vulkan/Context.h b/common/Vulkan/Context.h index ceb1eeefa5..ad38f7f3d2 100644 --- a/common/Vulkan/Context.h +++ b/common/Vulkan/Context.h @@ -70,7 +70,7 @@ namespace Vulkan // Creates a new context and sets it up as global. static bool Create(std::string_view gpu_name, const WindowInfo* wi, std::unique_ptr* out_swap_chain, - bool threaded_presentation, bool enable_debug_utils, bool enable_validation_layer); + bool vsync, bool threaded_presentation, bool enable_debug_utils, bool enable_validation_layer); // Destroys context. static void Destroy(); diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 9660c1cea2..5d4149e179 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -906,7 +906,7 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main) } if (!host_display->CreateRenderDevice(wi.value(), Host::GetStringSettingValue("EmuCore/GS", "Adapter", ""), - Host::GetBoolSettingValue("EmuCore/GS", "ThreadedPresentation", false), + EmuConfig.GetEffectiveVsyncMode(), Host::GetBoolSettingValue("EmuCore/GS", "ThreadedPresentation", false), Host::GetBoolSettingValue("EmuCore/GS", "UseDebugDevice", false))) { QMessageBox::critical(this, tr("Error"), tr("Failed to create host display device context.")); diff --git a/pcsx2/Frontend/D3D11HostDisplay.cpp b/pcsx2/Frontend/D3D11HostDisplay.cpp index 3b3d1f3939..5038b8965f 100644 --- a/pcsx2/Frontend/D3D11HostDisplay.cpp +++ b/pcsx2/Frontend/D3D11HostDisplay.cpp @@ -205,10 +205,10 @@ bool D3D11HostDisplay::GetHostRefreshRate(float* refresh_rate) void D3D11HostDisplay::SetVSync(VsyncMode mode) { - m_vsync = mode; + m_vsync_mode = mode; } -bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) +bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) { UINT create_flags = 0; if (debug_device) @@ -318,6 +318,7 @@ bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view } m_window_info = wi; + m_vsync_mode = vsync; return true; } @@ -741,7 +742,7 @@ void D3D11HostDisplay::EndPresent() ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - const UINT vsync_rate = static_cast(m_vsync != VsyncMode::Off); + const UINT vsync_rate = static_cast(m_vsync_mode != VsyncMode::Off); if (vsync_rate == 0 && m_using_allow_tearing) m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); else diff --git a/pcsx2/Frontend/D3D11HostDisplay.h b/pcsx2/Frontend/D3D11HostDisplay.h index bef98794fe..c222148e83 100644 --- a/pcsx2/Frontend/D3D11HostDisplay.h +++ b/pcsx2/Frontend/D3D11HostDisplay.h @@ -42,7 +42,7 @@ public: bool HasRenderDevice() const override; bool HasRenderSurface() const override; - bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) override; + bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override; bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override; void DestroyRenderDevice() override; @@ -89,7 +89,6 @@ protected: ComPtr m_swap_chain; ComPtr m_swap_chain_rtv; - VsyncMode m_vsync = VsyncMode::Off; bool m_allow_tearing_supported = false; bool m_using_flip_model_swap_chain = true; bool m_using_allow_tearing = false; diff --git a/pcsx2/Frontend/OpenGLHostDisplay.cpp b/pcsx2/Frontend/OpenGLHostDisplay.cpp index 849080894c..4e2d2c6687 100644 --- a/pcsx2/Frontend/OpenGLHostDisplay.cpp +++ b/pcsx2/Frontend/OpenGLHostDisplay.cpp @@ -190,7 +190,7 @@ bool OpenGLHostDisplay::HasRenderSurface() const return m_window_info.type != WindowInfo::Type::Surfaceless; } -bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) +bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) { m_gl_context = GL::Context::Create(wi); if (!m_gl_context) @@ -201,17 +201,23 @@ bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_vie } m_window_info = m_gl_context->GetWindowInfo(); + m_vsync_mode = vsync; return true; } bool OpenGLHostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) { - // Start with vsync off. - m_gl_context->SetSwapInterval(0); + SetSwapInterval(); GL::Program::ResetLastProgram(); return true; } +void OpenGLHostDisplay::SetSwapInterval() +{ + const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0)); + m_gl_context->SetSwapInterval(interval); +} + bool OpenGLHostDisplay::MakeRenderContextCurrent() { if (!m_gl_context->MakeCurrent()) @@ -220,6 +226,7 @@ bool OpenGLHostDisplay::MakeRenderContextCurrent() return false; } + SetSwapInterval(); return true; } diff --git a/pcsx2/Frontend/OpenGLHostDisplay.h b/pcsx2/Frontend/OpenGLHostDisplay.h index 42bc9cc32e..5d8d351784 100644 --- a/pcsx2/Frontend/OpenGLHostDisplay.h +++ b/pcsx2/Frontend/OpenGLHostDisplay.h @@ -36,7 +36,7 @@ public: bool HasRenderDevice() const override; bool HasRenderSurface() const override; - bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) override; + bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override; bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override; void DestroyRenderDevice() override; @@ -68,8 +68,8 @@ protected: void DestroyImGuiContext() override; bool UpdateImGuiFontTexture() override; - std::unique_ptr m_gl_context; + void SetSwapInterval(); - VsyncMode m_vsync_mode = VsyncMode::Off; + std::unique_ptr m_gl_context; }; diff --git a/pcsx2/Frontend/VulkanHostDisplay.cpp b/pcsx2/Frontend/VulkanHostDisplay.cpp index 51a319e643..474435683e 100644 --- a/pcsx2/Frontend/VulkanHostDisplay.cpp +++ b/pcsx2/Frontend/VulkanHostDisplay.cpp @@ -85,7 +85,7 @@ bool VulkanHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi) return false; } - m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, false); + m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, m_vsync_mode != VsyncMode::Off); if (!m_swap_chain) { Console.Error("Failed to create swap chain"); @@ -216,29 +216,32 @@ void VulkanHostDisplay::UpdateTexture( void VulkanHostDisplay::SetVSync(VsyncMode mode) { - if (!m_swap_chain) + if (!m_swap_chain || m_vsync_mode == mode) return; // This swap chain should not be used by the current buffer, thus safe to destroy. g_vulkan_context->WaitForGPUIdle(); m_swap_chain->SetVSync(mode != VsyncMode::Off); + m_vsync_mode = mode; } bool VulkanHostDisplay::CreateRenderDevice( - const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) + const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) { // debug_device = true; WindowInfo local_wi(wi); if (!Vulkan::Context::Create( - adapter_name, &local_wi, &m_swap_chain, threaded_presentation, debug_device, debug_device)) + adapter_name, &local_wi, &m_swap_chain, vsync != VsyncMode::Off, threaded_presentation, debug_device, debug_device)) { Console.Error("Failed to create Vulkan context"); m_window_info = {}; return false; } + // NOTE: This is assigned afterwards, because some platforms can modify the window info (e.g. Metal). m_window_info = m_swap_chain ? m_swap_chain->GetWindowInfo() : local_wi; + m_vsync_mode = vsync; return true; } diff --git a/pcsx2/Frontend/VulkanHostDisplay.h b/pcsx2/Frontend/VulkanHostDisplay.h index 88b30cd67a..7abc91e980 100644 --- a/pcsx2/Frontend/VulkanHostDisplay.h +++ b/pcsx2/Frontend/VulkanHostDisplay.h @@ -27,7 +27,7 @@ public: bool HasRenderDevice() const override; bool HasRenderSurface() const override; - bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) override; + bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override; bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override; void DestroyRenderDevice() override; diff --git a/pcsx2/HostDisplay.h b/pcsx2/HostDisplay.h index edb7db61ce..5db2146c64 100644 --- a/pcsx2/HostDisplay.h +++ b/pcsx2/HostDisplay.h @@ -95,7 +95,7 @@ public: virtual bool HasRenderDevice() const = 0; virtual bool HasRenderSurface() const = 0; - virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) = 0; + virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) = 0; virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) = 0; virtual bool MakeRenderContextCurrent() = 0; virtual bool DoneRenderContextCurrent() = 0; @@ -139,6 +139,7 @@ public: protected: WindowInfo m_window_info; Alignment m_display_alignment = Alignment::Center; + VsyncMode m_vsync_mode = VsyncMode::Off; }; namespace Host diff --git a/pcsx2/gui/AppHost.cpp b/pcsx2/gui/AppHost.cpp index 0adc1b299c..3d24e9d58a 100644 --- a/pcsx2/gui/AppHost.cpp +++ b/pcsx2/gui/AppHost.cpp @@ -120,7 +120,8 @@ HostDisplay* Host::AcquireHostDisplay(HostDisplay::RenderAPI api) if (!s_host_display) return nullptr; - if (!s_host_display->CreateRenderDevice(g_gs_window_info, GSConfig.Adapter, GSConfig.ThreadedPresentation, GSConfig.UseDebugDevice) || + if (!s_host_display->CreateRenderDevice(g_gs_window_info, GSConfig.Adapter, EmuConfig.GetEffectiveVsyncMode(), + GSConfig.ThreadedPresentation, GSConfig.UseDebugDevice) || !s_host_display->InitializeRenderDevice(StringUtil::wxStringToUTF8String(EmuFolders::Cache.ToString()), GSConfig.UseDebugDevice) || !ImGuiManager::Initialize()) {