GS: Make VSync a boolean toggle

i.e. ditch the old adaptive mode, and always use adaptive if available.
This commit is contained in:
Stenzek 2024-05-15 08:50:15 +10:00 committed by Connor McLaughlin
parent 81203d9a15
commit 9fac941570
29 changed files with 156 additions and 148 deletions

View File

@ -107,7 +107,7 @@ bool GSRunner::InitializeConfig()
// complete as quickly as possible // complete as quickly as possible
si.SetBoolValue("EmuCore/GS", "FrameLimitEnable", false); si.SetBoolValue("EmuCore/GS", "FrameLimitEnable", false);
si.SetIntValue("EmuCore/GS", "VsyncEnable", static_cast<int>(VsyncMode::Off)); si.SetIntValue("EmuCore/GS", "VsyncEnable", false);
// ensure all input sources are disabled, we're not using them // ensure all input sources are disabled, we're not using them
si.SetBoolValue("InputSources", "SDL", false); si.SetBoolValue("InputSources", "SDL", false);

View File

@ -190,13 +190,6 @@ enum class SpeedHack
MaxCount, MaxCount,
}; };
enum class VsyncMode
{
Off,
On,
Adaptive,
};
enum class AspectRatioType : u8 enum class AspectRatioType : u8
{ {
Stretch, Stretch,
@ -596,6 +589,8 @@ struct Pcsx2Config
struct struct
{ {
bool bool
SynchronousMTGS : 1,
VsyncEnable : 1,
PCRTCAntiBlur : 1, PCRTCAntiBlur : 1,
DisableInterlaceOffset : 1, DisableInterlaceOffset : 1,
PCRTCOffsets : 1, PCRTCOffsets : 1,
@ -618,9 +613,7 @@ struct Pcsx2Config
OsdShowIndicators : 1, OsdShowIndicators : 1,
OsdShowSettings : 1, OsdShowSettings : 1,
OsdShowInputs : 1, OsdShowInputs : 1,
OsdShowFrameTimes : 1; OsdShowFrameTimes : 1,
bool
HWSpinGPUForReadbacks : 1, HWSpinGPUForReadbacks : 1,
HWSpinCPUForReadbacks : 1, HWSpinCPUForReadbacks : 1,
GPUPaletteConversion : 1, GPUPaletteConversion : 1,
@ -664,12 +657,6 @@ struct Pcsx2Config
int VsyncQueueSize = 2; 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 FramerateNTSC = DEFAULT_FRAME_RATE_NTSC;
float FrameratePAL = DEFAULT_FRAME_RATE_PAL; float FrameratePAL = DEFAULT_FRAME_RATE_PAL;

View File

@ -471,7 +471,7 @@ void GSPresentCurrentFrame()
void GSThrottlePresentation() void GSThrottlePresentation()
{ {
if (g_gs_device->GetVsyncMode() != VsyncMode::Off) if (g_gs_device->IsVSyncEnabled())
{ {
// Let vsync take care of throttling. // Let vsync take care of throttling.
return; return;
@ -528,9 +528,9 @@ void GSUpdateDisplayWindow()
ImGuiManager::WindowResized(); ImGuiManager::WindowResized();
} }
void GSSetVSyncMode(VsyncMode mode) void GSSetVSyncEnabled(bool enabled)
{ {
g_gs_device->SetVSync(mode); g_gs_device->SetVSyncEnabled(enabled);
} }
bool GSWantsExclusiveFullscreen() bool GSWantsExclusiveFullscreen()

View File

@ -84,7 +84,7 @@ void GSSetDisplayAlignment(GSDisplayAlignment alignment);
bool GSHasDisplayWindow(); bool GSHasDisplayWindow();
void GSResizeDisplayWindow(int width, int height, float scale); void GSResizeDisplayWindow(int width, int height, float scale);
void GSUpdateDisplayWindow(); void GSUpdateDisplayWindow();
void GSSetVSyncMode(VsyncMode mode); void GSSetVSyncEnabled(bool enabled);
GSRendererType GSGetCurrentRenderer(); GSRendererType GSGetCurrentRenderer();
bool GSIsHardwareRenderer(); bool GSIsHardwareRenderer();
@ -127,7 +127,7 @@ namespace Host
void SetFullscreen(bool enabled); void SetFullscreen(bool enabled);
/// Returns the desired vsync mode, depending on the runtime environment. /// 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. /// Called when video capture starts or stops. Called on the MTGS thread.
void OnCaptureStarted(const std::string& filename); void OnCaptureStarted(const std::string& filename);

View File

@ -307,7 +307,7 @@ int GSDevice::GetMipmapLevelsForSize(int width, int height)
bool GSDevice::Create() bool GSDevice::Create()
{ {
m_vsync_mode = Host::GetEffectiveVSyncMode(); m_vsync_enabled = Host::IsVsyncEffectivelyEnabled();
return true; return true;
} }

View File

@ -803,7 +803,6 @@ protected:
static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u16) * 16383 * 6; static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u16) * 16383 * 6;
WindowInfo m_window_info; WindowInfo m_window_info;
VsyncMode m_vsync_mode = VsyncMode::Off;
GSTexture* m_imgui_font = nullptr; GSTexture* m_imgui_font = nullptr;
@ -824,6 +823,7 @@ protected:
u32 start, count; u32 start, count;
} m_index = {}; } m_index = {};
unsigned int m_frame = 0; // for ageing the pool unsigned int m_frame = 0; // for ageing the pool
bool m_vsync_enabled = false;
bool m_rbswapped = false; bool m_rbswapped = false;
FeatureSupport m_features; FeatureSupport m_features;
@ -874,7 +874,7 @@ public:
__fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); } __fi s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
__fi GSVector2i GetWindowSize() const { return GSVector2i(static_cast<s32>(m_window_info.surface_width), static_cast<s32>(m_window_info.surface_height)); } __fi GSVector2i GetWindowSize() const { return GSVector2i(static_cast<s32>(m_window_info.surface_width), static_cast<s32>(m_window_info.surface_height)); }
__fi float GetWindowScale() const { return m_window_info.surface_scale; } __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; } __fi GSTexture* GetCurrent() const { return m_current; }
@ -915,7 +915,7 @@ public:
virtual void EndPresent() = 0; virtual void EndPresent() = 0;
/// Changes vsync mode for this display. /// 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. /// Returns the effective refresh rate of this display.
virtual bool GetHostRefreshRate(float* refresh_rate); virtual bool GetHostRefreshRate(float* refresh_rate);

View File

@ -628,9 +628,9 @@ bool GSDevice11::GetHostRefreshRate(float* refresh_rate)
return GSDevice::GetHostRefreshRate(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() 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 // 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 // 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. // 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(); PopTimestampQuery();
m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), s_present_clear_color.data()); m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), s_present_clear_color.data());
@ -957,14 +957,13 @@ void GSDevice11::EndPresent()
RenderImGui(); RenderImGui();
// See note in BeginPresent() for why it's conditional on vsync-off. // See note in BeginPresent() for why it's conditional on vsync-off.
const bool vsync_on = m_vsync_mode != VsyncMode::Off; if (!m_vsync_enabled && m_gpu_timing_enabled)
if (!vsync_on && m_gpu_timing_enabled)
PopTimestampQuery(); 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); m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
else else
m_swap_chain->Present(static_cast<UINT>(vsync_on), 0); m_swap_chain->Present(static_cast<UINT>(m_vsync_enabled), 0);
if (m_gpu_timing_enabled) if (m_gpu_timing_enabled)
KickTimestampQuery(); KickTimestampQuery();

View File

@ -273,7 +273,7 @@ public:
bool GetHostRefreshRate(float* refresh_rate) override; bool GetHostRefreshRate(float* refresh_rate) override;
void SetVSync(VsyncMode mode) override; void SetVSyncEnabled(bool enabled) override;
PresentResult BeginPresent(bool frame_skip) override; PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override; void EndPresent() override;

View File

@ -774,9 +774,9 @@ bool GSDevice12::GetHostRefreshRate(float* refresh_rate)
return GSDevice::GetHostRefreshRate(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() bool GSDevice12::CreateSwapChain()
@ -1109,11 +1109,10 @@ void GSDevice12::EndPresent()
return; return;
} }
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off); if (!m_vsync_enabled && m_using_allow_tearing)
if (!vsync && m_using_allow_tearing)
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
else else
m_swap_chain->Present(static_cast<UINT>(vsync), 0); m_swap_chain->Present(static_cast<UINT>(m_vsync_enabled), 0);
InvalidateCachedState(); InvalidateCachedState();
} }

View File

@ -409,7 +409,7 @@ public:
bool GetHostRefreshRate(float* refresh_rate) override; bool GetHostRefreshRate(float* refresh_rate) override;
void SetVSync(VsyncMode mode) override; void SetVSyncEnabled(bool enabled) override;
PresentResult BeginPresent(bool frame_skip) override; PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override; void EndPresent() override;

View File

@ -392,7 +392,7 @@ public:
PresentResult BeginPresent(bool frame_skip) override; PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override; void EndPresent() override;
void SetVSync(VsyncMode mode) override; void SetVSyncEnabled(bool enabled) override;
bool GetHostRefreshRate(float* refresh_rate) override; bool GetHostRefreshRate(float* refresh_rate) override;

View File

@ -877,7 +877,7 @@ bool GSDeviceMTL::Create()
{ {
AttachSurfaceOnMainThread(); AttachSurfaceOnMainThread();
}); });
[m_layer setDisplaySyncEnabled:m_vsync_mode != VsyncMode::Off]; [m_layer setDisplaySyncEnabled:m_vsync_enabled];
} }
else else
{ {
@ -1305,7 +1305,7 @@ void GSDeviceMTL::EndPresent()
if (m_current_drawable) if (m_current_drawable)
{ {
const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always || 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) if (use_present_drawable)
[m_current_render_cmdbuf presentDrawable:m_current_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; return;
[m_layer setDisplaySyncEnabled:mode != VsyncMode::Off]; [m_layer setDisplaySyncEnabled:enabled];
m_vsync_mode = mode; m_vsync_enabled = enabled;
} }
bool GSDeviceMTL::GetHostRefreshRate(float* refresh_rate) bool GSDeviceMTL::GetHostRefreshRate(float* refresh_rate)

View File

@ -35,6 +35,7 @@ public:
virtual bool IsCurrent() = 0; virtual bool IsCurrent() = 0;
virtual bool MakeCurrent() = 0; virtual bool MakeCurrent() = 0;
virtual bool DoneCurrent() = 0; virtual bool DoneCurrent() = 0;
virtual bool SupportsNegativeSwapInterval() const = 0;
virtual bool SetSwapInterval(s32 interval) = 0; virtual bool SetSwapInterval(s32 interval) = 0;
virtual std::unique_ptr<GLContext> CreateSharedContext(const WindowInfo& wi, Error* error) = 0; virtual std::unique_ptr<GLContext> CreateSharedContext(const WindowInfo& wi, Error* error) = 0;

View File

@ -312,6 +312,11 @@ bool GLContextEGL::DoneCurrent()
return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 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) bool GLContextEGL::SetSwapInterval(s32 interval)
{ {
return eglSwapInterval(m_display, 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); 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_config = config.value();
m_version = version; m_version = version;
return true; return true;

View File

@ -24,6 +24,7 @@ public:
bool IsCurrent() override; bool IsCurrent() override;
bool MakeCurrent() override; bool MakeCurrent() override;
bool DoneCurrent() override; bool DoneCurrent() override;
bool SupportsNegativeSwapInterval() const override;
bool SetSwapInterval(s32 interval) override; bool SetSwapInterval(s32 interval) override;
virtual std::unique_ptr<GLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override; virtual std::unique_ptr<GLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;
@ -52,4 +53,5 @@ protected:
EGLConfig m_config = {}; EGLConfig m_config = {};
bool m_use_ext_platform_base = false; bool m_use_ext_platform_base = false;
bool m_supports_negative_swap_interval = false;
}; };

View File

@ -152,6 +152,11 @@ bool GLContextWGL::DoneCurrent()
return wglMakeCurrent(m_dc, nullptr); 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) bool GLContextWGL::SetSwapInterval(s32 interval)
{ {
if (!GLAD_WGL_EXT_swap_control) if (!GLAD_WGL_EXT_swap_control)

View File

@ -27,6 +27,7 @@ public:
bool IsCurrent() override; bool IsCurrent() override;
bool MakeCurrent() override; bool MakeCurrent() override;
bool DoneCurrent() override; bool DoneCurrent() override;
bool SupportsNegativeSwapInterval() const override;
bool SetSwapInterval(s32 interval) override; bool SetSwapInterval(s32 interval) override;
std::unique_ptr<GLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override; std::unique_ptr<GLContext> CreateSharedContext(const WindowInfo& wi, Error* error) override;

View File

@ -146,21 +146,13 @@ bool GSDeviceOGL::HasSurface() const
return m_window_info.type != WindowInfo::Type::Surfaceless; 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; return;
// Window framebuffer has to be bound to call SetSwapInterval. m_vsync_enabled = enabled;
GLint current_fbo = 0; SetSwapInterval();
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
if (mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(mode != VsyncMode::Off));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
m_vsync_mode = mode;
} }
bool GSDeviceOGL::Create() bool GSDeviceOGL::Create()
@ -777,8 +769,19 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
void GSDeviceOGL::SetSwapInterval() void GSDeviceOGL::SetSwapInterval()
{ {
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0)); if (m_window_info.type == WindowInfo::Type::Surfaceless)
m_gl_context->SetSwapInterval(interval); 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, &current_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() void GSDeviceOGL::DestroyResources()
@ -868,9 +871,7 @@ bool GSDeviceOGL::UpdateWindow()
if (m_window_info.type != WindowInfo::Type::Surfaceless) if (m_window_info.type != WindowInfo::Type::Surfaceless)
{ {
// reset vsync rate, since it (usually) gets lost // reset vsync rate, since it (usually) gets lost
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1)) SetSwapInterval();
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
RenderBlankFrame(); RenderBlankFrame();
} }

View File

@ -292,7 +292,7 @@ public:
void DestroySurface() override; void DestroySurface() override;
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
void SetVSync(VsyncMode mode) override; void SetVSyncEnabled(bool enabled) override;
PresentResult BeginPresent(bool frame_skip) override; PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override; void EndPresent() override;

View File

@ -2222,7 +2222,7 @@ bool GSDeviceVK::UpdateWindow()
return false; 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)); Pcsx2Config::GSOptions::TriStateToOptionalBoolean(GSConfig.ExclusiveFullscreenControl));
if (!m_swap_chain) if (!m_swap_chain)
{ {
@ -2297,24 +2297,27 @@ std::string GSDeviceVK::GetDriverInfo() const
return ret; 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; return;
}
// This swap chain should not be used by the current buffer, thus safe to destroy. // This swap chain should not be used by the current buffer, thus safe to destroy.
WaitForGPUIdle(); WaitForGPUIdle();
if (!m_swap_chain->SetVSync(mode)) if (!m_swap_chain->SetVSyncEnabled(enabled))
{ {
// Try switching back to the old mode.. // 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"); pxFailRel("Failed to reset old vsync mode after failure");
m_swap_chain.reset(); m_swap_chain.reset();
} }
} }
m_vsync_mode = mode; m_vsync_enabled = enabled;
} }
GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip) GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
@ -2613,7 +2616,7 @@ bool GSDeviceVK::CreateDeviceAndSwapChain()
if (surface != VK_NULL_HANDLE) 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)); Pcsx2Config::GSOptions::TriStateToOptionalBoolean(GSConfig.ExclusiveFullscreenControl));
if (!m_swap_chain) if (!m_swap_chain)
{ {

View File

@ -524,7 +524,7 @@ public:
void DestroySurface() override; void DestroySurface() override;
std::string GetDriverInfo() const override; std::string GetDriverInfo() const override;
void SetVSync(VsyncMode mode) override; void SetVSyncEnabled(bool enabled) override;
PresentResult BeginPresent(bool frame_skip) override; PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override; void EndPresent() override;

View File

@ -18,11 +18,11 @@
#endif #endif
VKSwapChain::VKSwapChain( VKSwapChain::VKSwapChain(
const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional<bool> exclusive_fullscreen_control) const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional<bool> exclusive_fullscreen_control)
: m_window_info(wi) : m_window_info(wi)
, m_surface(surface) , m_surface(surface)
, m_vsync_mode(vsync)
, m_exclusive_fullscreen_control(exclusive_fullscreen_control) , 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> VKSwapChain::Create( std::unique_ptr<VKSwapChain> VKSwapChain::Create(
const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional<bool> exclusive_fullscreen_control) const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional<bool> exclusive_fullscreen_control)
{ {
std::unique_ptr<VKSwapChain> swap_chain = std::unique_ptr<VKSwapChain> swap_chain =
std::unique_ptr<VKSwapChain>(new VKSwapChain(wi, surface, vsync, exclusive_fullscreen_control)); std::unique_ptr<VKSwapChain>(new VKSwapChain(wi, surface, vsync, exclusive_fullscreen_control));
@ -226,17 +226,7 @@ static const char* PresentModeToString(VkPresentModeKHR mode)
} }
} }
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(VsyncMode mode) std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_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<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surface, VsyncMode vsync)
{ {
VkResult res; VkResult res;
u32 mode_count; u32 mode_count;
@ -260,31 +250,30 @@ std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surf
return it != present_modes.end(); return it != present_modes.end();
}; };
// Use preferred mode if available. // Use preferred mode if available.
const VkPresentModeKHR preferred_mode = GetPreferredPresentModeForVsyncMode(vsync); VkPresentModeKHR selected_mode;
VkPresentModeKHR selected_mode; if (CheckForMode(requested_mode))
if (CheckForMode(preferred_mode)) {
{ selected_mode = requested_mode;
selected_mode = preferred_mode; }
} else if (requested_mode != VK_PRESENT_MODE_FIFO_KHR && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
else if (vsync != VsyncMode::On && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR)) {
{ // Prefer mailbox over fifo for adaptive vsync/no-vsync. This way it'll only delay one frame.
// Prefer mailbox over fifo for adaptive vsync/no-vsync. selected_mode = VK_PRESENT_MODE_MAILBOX_KHR;
selected_mode = VK_PRESENT_MODE_MAILBOX_KHR; }
} else if (requested_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR && CheckForMode(VK_PRESENT_MODE_FIFO_KHR))
else if (vsync != VsyncMode::Off && CheckForMode(VK_PRESENT_MODE_FIFO_KHR)) {
{ // Fallback to FIFO if we're using any kind of vsync.
// Fallback to FIFO if we're using any kind of vsync. // This should never fail, FIFO is mandated.
// This should never fail, FIFO is mandated. selected_mode = VK_PRESENT_MODE_FIFO_KHR;
selected_mode = VK_PRESENT_MODE_FIFO_KHR; }
} else
else {
{ // Fall back to whatever is available.
// Fall back to whatever is available. selected_mode = present_modes[0];
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)); PresentModeToString(selected_mode));
return selected_mode; return selected_mode;
@ -294,7 +283,11 @@ bool VKSwapChain::CreateSwapChain()
{ {
// Select swap chain format and present mode // Select swap chain format and present mode
std::optional<VkSurfaceFormatKHR> surface_format = SelectSurfaceFormat(m_surface); std::optional<VkSurfaceFormatKHR> surface_format = SelectSurfaceFormat(m_surface);
std::optional<VkPresentModeKHR> 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<VkPresentModeKHR> present_mode = SelectPresentMode(m_surface, requested_mode);
if (!surface_format.has_value() || !present_mode.has_value()) if (!surface_format.has_value() || !present_mode.has_value())
return false; return false;
@ -414,6 +407,7 @@ bool VKSwapChain::CreateSwapChain()
m_window_info.surface_width = std::max(1u, size.width); m_window_info.surface_width = std::max(1u, size.width);
m_window_info.surface_height = std::max(1u, size.height); m_window_info.surface_height = std::max(1u, size.height);
m_actual_present_mode = present_mode.value();
// Get and create images. // Get and create images.
pxAssert(m_images.empty()); pxAssert(m_images.empty());
@ -558,12 +552,12 @@ bool VKSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_scale
return true; return true;
} }
bool VKSwapChain::SetVSync(VsyncMode mode) bool VKSwapChain::SetVSyncEnabled(bool enabled)
{ {
if (m_vsync_mode == mode) if (m_vsync_enabled == enabled)
return true; return true;
m_vsync_mode = mode; m_vsync_enabled = enabled;
// Recreate the swap chain with the new present mode. // Recreate the swap chain with the new present mode.
DevCon.WriteLn("Recreating swap chain to change present mode."); DevCon.WriteLn("Recreating swap chain to change present mode.");

View File

@ -25,7 +25,7 @@ public:
// Create a new swap chain from a pre-existing surface. // Create a new swap chain from a pre-existing surface.
static std::unique_ptr<VKSwapChain> Create( static std::unique_ptr<VKSwapChain> Create(
const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional<bool> exclusive_fullscreen_control); const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional<bool> exclusive_fullscreen_control);
__fi VkSurfaceKHR GetSurface() const { return m_surface; } __fi VkSurfaceKHR GetSurface() const { return m_surface; }
__fi VkSwapchainKHR GetSwapChain() const { return m_swap_chain; } __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). // 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; VkFormat GetTextureFormat() const;
VkResult AcquireNextImage(); VkResult AcquireNextImage();
@ -67,14 +71,14 @@ public:
bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f); 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. // Change vsync enabled state. This may fail as it causes a swapchain recreation.
bool SetVSync(VsyncMode mode); bool SetVSyncEnabled(bool enabled);
private: private:
VKSwapChain( VKSwapChain(
const WindowInfo& wi, VkSurfaceKHR surface, VsyncMode vsync, std::optional<bool> exclusive_fullscreen_control); const WindowInfo& wi, VkSurfaceKHR surface, bool vsync, std::optional<bool> exclusive_fullscreen_control);
static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface); static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface);
static std::optional<VkPresentModeKHR> SelectPresentMode(VkSurfaceKHR surface, VsyncMode vsync); static std::optional<VkPresentModeKHR> SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_mode);
bool CreateSwapChain(); bool CreateSwapChain();
void DestroySwapChain(); void DestroySwapChain();
@ -98,10 +102,11 @@ private:
std::vector<std::unique_ptr<GSTextureVK>> m_images; std::vector<std::unique_ptr<GSTextureVK>> m_images;
std::vector<ImageSemaphores> m_semaphores; std::vector<ImageSemaphores> 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_image = 0;
u32 m_current_semaphore = 0; u32 m_current_semaphore = 0;
std::optional<VkResult> m_image_acquire_result; std::optional<VkResult> m_image_acquire_result;
std::optional<bool> m_exclusive_fullscreen_control; std::optional<bool> m_exclusive_fullscreen_control;
bool m_vsync_enabled = false;
}; };

View File

@ -16,8 +16,6 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
enum class VsyncMode;
class SettingsInterface; class SettingsInterface;
namespace Host namespace Host

View File

@ -3482,11 +3482,6 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
"13", //GSRendererType::SW, "13", //GSRendererType::SW,
"11", //GSRendererType::Null "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[] = { static constexpr const char* s_bilinear_present_options[] = {
FSUI_NSTR("Off"), FSUI_NSTR("Off"),
FSUI_NSTR("Bilinear (Smooth)"), FSUI_NSTR("Bilinear (Smooth)"),
@ -3621,8 +3616,8 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
MenuHeading(FSUI_CSTR("Renderer")); MenuHeading(FSUI_CSTR("Renderer"));
DrawStringListSetting(bsi, FSUI_CSTR("Renderer"), FSUI_CSTR("Selects the API used to render the emulated GS."), "EmuCore/GS", 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); "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."), DrawToggleSetting(bsi, FSUI_CSTR("Sync To Host Refresh (VSync)"), FSUI_CSTR("Synchronizes frame presentation with host refresh."),
"EmuCore/GS", "VsyncEnable", static_cast<int>(VsyncMode::Off), s_vsync_values, std::size(s_vsync_values), true); "EmuCore/GS", "VsyncEnable", false);
MenuHeading(FSUI_CSTR("Display")); MenuHeading(FSUI_CSTR("Display"));
DrawStringListSetting(bsi, FSUI_CSTR("Aspect Ratio"), FSUI_CSTR("Selects the aspect ratio to display the game content at."), 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", "Software");
TRANSLATE_NOOP("FullscreenUI", "Null"); TRANSLATE_NOOP("FullscreenUI", "Null");
TRANSLATE_NOOP("FullscreenUI", "Off"); TRANSLATE_NOOP("FullscreenUI", "Off");
TRANSLATE_NOOP("FullscreenUI", "On");
TRANSLATE_NOOP("FullscreenUI", "Adaptive");
TRANSLATE_NOOP("FullscreenUI", "Bilinear (Smooth)"); TRANSLATE_NOOP("FullscreenUI", "Bilinear (Smooth)");
TRANSLATE_NOOP("FullscreenUI", "Bilinear (Sharp)"); TRANSLATE_NOOP("FullscreenUI", "Bilinear (Sharp)");
TRANSLATE_NOOP("FullscreenUI", "No Deinterlacing"); TRANSLATE_NOOP("FullscreenUI", "No Deinterlacing");

View File

@ -931,7 +931,7 @@ void MTGS::ApplySettings()
RunOnGSThread([opts = EmuConfig.GS]() { RunOnGSThread([opts = EmuConfig.GS]() {
GSUpdateConfig(opts); GSUpdateConfig(opts);
GSSetVSyncMode(Host::GetEffectiveVSyncMode()); GSSetVSyncEnabled(Host::IsVsyncEffectivelyEnabled());
}); });
// We need to synchronize the thread when changing any settings when the download mode // 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"); pxAssertRel(IsOpen(), "MTGS is running");
RunOnGSThread([mode]() { RunOnGSThread([enabled]() {
Console.WriteLn("Vsync is %s", mode == VsyncMode::Off ? "OFF" : (mode == VsyncMode::Adaptive ? "ADAPTIVE" : "ON")); INFO_LOG("Vsync is {}", enabled ? "ON" : "OFF");
GSSetVSyncMode(mode); 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 */) void MTGS::SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message /* = true */)

View File

@ -70,8 +70,8 @@ namespace MTGS
void ApplySettings(); void ApplySettings();
void ResizeDisplayWindow(int width, int height, float scale); void ResizeDisplayWindow(int width, int height, float scale);
void UpdateDisplayWindow(); void UpdateDisplayWindow();
void SetVSyncMode(VsyncMode mode); void SetVSyncEnabled(bool enabled);
void UpdateVSyncMode(); void UpdateVSyncEnabled();
void SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message = true); void SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message = true);
void ToggleSoftwareRendering(); void ToggleSoftwareRendering();
bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,

View File

@ -793,11 +793,12 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapSection("EmuCore/GS"); SettingsWrapSection("EmuCore/GS");
#ifdef PCSX2_DEVBUILD #ifdef PCSX2_DEVBUILD
SettingsWrapEntry(SynchronousMTGS); SettingsWrapBitBool(SynchronousMTGS);
#endif #endif
SettingsWrapEntry(VsyncQueueSize);
wrap.EnumEntry(CURRENT_SETTINGS_SECTION, "VsyncEnable", VsyncEnable, NULL, VsyncEnable); SettingsWrapBitBool(VsyncEnable);
SettingsWrapEntry(VsyncQueueSize);
SettingsWrapEntry(FramerateNTSC); SettingsWrapEntry(FramerateNTSC);
SettingsWrapEntry(FrameratePAL); SettingsWrapEntry(FrameratePAL);

View File

@ -2004,7 +2004,7 @@ double VMManager::AdjustToHostRefreshRate(float frame_rate, float target_speed)
const float ratio = host_refresh_rate / frame_rate; const float ratio = host_refresh_rate / frame_rate;
const bool syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f); const bool syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f);
s_target_speed_synced_to_host = syncing_to_host; 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, 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"); 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; s_target_speed = target_speed;
MTGS::UpdateVSyncMode(); MTGS::UpdateVSyncEnabled();
SPU2::OnTargetSpeedChanged(); SPU2::OnTargetSpeedChanged();
ResetFrameLimiter(); ResetFrameLimiter();
} }
@ -2546,13 +2546,13 @@ void VMManager::SetPaused(bool paused)
SetState(paused ? VMState::Paused : VMState::Running); SetState(paused ? VMState::Paused : VMState::Running);
} }
VsyncMode Host::GetEffectiveVSyncMode() bool Host::IsVsyncEffectivelyEnabled()
{ {
const bool has_vm = VMManager::GetState() != VMState::Shutdown; const bool has_vm = VMManager::GetState() != VMState::Shutdown;
// Force vsync off when not running at 100% speed. // Force vsync off when not running at 100% speed.
if (has_vm && (s_target_speed != 1.0f && !s_use_vsync_for_timing)) if (has_vm && (s_target_speed != 1.0f && !s_use_vsync_for_timing))
return VsyncMode::Off; return false;
// Otherwise use the config setting. // Otherwise use the config setting.
return EmuConfig.GS.VsyncEnable; 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. // Still need to update target speed, because of sync-to-host-refresh.
UpdateTargetSpeed(); UpdateTargetSpeed();
MTGS::UpdateVSyncEnabled();
} }
MTGS::ApplySettings(); MTGS::ApplySettings();