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
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
si.SetBoolValue("InputSources", "SDL", false);

View File

@ -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;

View File

@ -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()

View File

@ -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);

View File

@ -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;
}

View File

@ -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<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 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);

View File

@ -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<UINT>(vsync_on), 0);
m_swap_chain->Present(static_cast<UINT>(m_vsync_enabled), 0);
if (m_gpu_timing_enabled)
KickTimestampQuery();

View File

@ -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;

View File

@ -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<UINT>(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<UINT>(vsync), 0);
m_swap_chain->Present(static_cast<UINT>(m_vsync_enabled), 0);
InvalidateCachedState();
}

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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<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);
}
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;

View File

@ -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<GLContext> 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;
};

View File

@ -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)

View File

@ -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<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;
}
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, &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;
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, &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()
@ -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<s32>(m_vsync_mode != VsyncMode::Off));
SetSwapInterval();
RenderBlankFrame();
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -18,11 +18,11 @@
#endif
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_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> 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>(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<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surface, VsyncMode vsync)
std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_mode)
{
VkResult res;
u32 mode_count;
@ -261,18 +251,17 @@ std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surf
};
// Use preferred mode if available.
const VkPresentModeKHR preferred_mode = GetPreferredPresentModeForVsyncMode(vsync);
VkPresentModeKHR selected_mode;
if (CheckForMode(preferred_mode))
if (CheckForMode(requested_mode))
{
selected_mode = preferred_mode;
selected_mode = requested_mode;
}
else if (vsync != VsyncMode::On && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
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 (vsync != VsyncMode::Off && CheckForMode(VK_PRESENT_MODE_FIFO_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.
@ -284,7 +273,7 @@ std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surf
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<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())
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.");

View File

@ -25,7 +25,7 @@ public:
// Create a new swap chain from a pre-existing surface.
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 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<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<VkPresentModeKHR> SelectPresentMode(VkSurfaceKHR surface, VsyncMode vsync);
static std::optional<VkPresentModeKHR> SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_mode);
bool CreateSwapChain();
void DestroySwapChain();
@ -98,10 +102,11 @@ private:
std::vector<std::unique_ptr<GSTextureVK>> m_images;
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_semaphore = 0;
std::optional<VkResult> m_image_acquire_result;
std::optional<bool> m_exclusive_fullscreen_control;
bool m_vsync_enabled = false;
};

View File

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

View File

@ -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<int>(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");

View File

@ -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 */)

View File

@ -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,

View File

@ -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);

View File

@ -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();