GPU: Further improve vsync handling
This commit is contained in:
@ -201,7 +201,7 @@ u32 D3D11Device::GetSwapChainBufferCount() const
|
||||
{
|
||||
// With vsync off, we only need two buffers. Same for blocking vsync.
|
||||
// With triple buffering, we need three.
|
||||
return (m_vsync_enabled && m_vsync_prefer_triple_buffer) ? 3 : 2;
|
||||
return (m_vsync_mode == GPUVSyncMode::TripleBuffered) ? 3 : 2;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateSwapChain()
|
||||
@ -357,10 +357,6 @@ bool D3D11Device::CreateSwapChainRTV()
|
||||
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window_info.surface_refresh_rate = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -592,31 +588,13 @@ void D3D11Device::InvalidateRenderTarget(GPUTexture* t)
|
||||
static_cast<D3D11Texture*>(t)->CommitClear(m_context.Get());
|
||||
}
|
||||
|
||||
std::optional<float> D3D11Device::GetHostRefreshRate()
|
||||
void D3D11Device::SetVSyncMode(GPUVSyncMode mode)
|
||||
{
|
||||
if (m_swap_chain && m_is_exclusive_fullscreen)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||
{
|
||||
DEV_LOG("using fs rr: {} {}", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
|
||||
return static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
}
|
||||
|
||||
return GPUDevice::GetHostRefreshRate();
|
||||
}
|
||||
|
||||
void D3D11Device::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||
{
|
||||
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||
if (m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
const u32 old_buffer_count = GetSwapChainBufferCount();
|
||||
m_vsync_enabled = enabled;
|
||||
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||
m_vsync_mode = mode;
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
@ -656,7 +634,7 @@ bool D3D11Device::BeginPresent(bool skip_present)
|
||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
|
||||
// the time, when it's more like a couple of percent.
|
||||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||
if (IsVSyncModeBlocking() && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
@ -674,12 +652,12 @@ void D3D11Device::EndPresent(bool explicit_present)
|
||||
DebugAssert(!explicit_present);
|
||||
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||
|
||||
if (!m_vsync_enabled && m_gpu_timing_enabled)
|
||||
if (!IsVSyncModeBlocking() && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
// DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it.
|
||||
if (m_vsync_enabled)
|
||||
m_swap_chain->Present(BoolToUInt32(1), 0);
|
||||
if (IsVSyncModeBlocking())
|
||||
m_swap_chain->Present(1, 0);
|
||||
else if (m_using_allow_tearing) // Disabled or VRR, VRR requires the allow tearing flag :/
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
else
|
||||
|
||||
@ -96,8 +96,7 @@ public:
|
||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||
|
||||
std::optional<float> GetHostRefreshRate() override;
|
||||
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||
void SetVSyncMode(GPUVSyncMode mode) override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
@ -801,7 +801,7 @@ u32 D3D12Device::GetSwapChainBufferCount() const
|
||||
{
|
||||
// With vsync off, we only need two buffers. Same for blocking vsync.
|
||||
// With triple buffering, we need three.
|
||||
return (m_vsync_enabled && m_vsync_prefer_triple_buffer) ? 3 : 2;
|
||||
return (m_vsync_mode == GPUVSyncMode::TripleBuffered) ? 3 : 2;
|
||||
}
|
||||
|
||||
bool D3D12Device::CreateSwapChain()
|
||||
@ -942,10 +942,6 @@ bool D3D12Device::CreateSwapChainRTV()
|
||||
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_window_info.surface_refresh_rate = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_current_swap_chain_buffer = 0;
|
||||
@ -1088,31 +1084,13 @@ std::string D3D12Device::GetDriverInfo() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<float> D3D12Device::GetHostRefreshRate()
|
||||
void D3D12Device::SetVSyncMode(GPUVSyncMode mode)
|
||||
{
|
||||
if (m_swap_chain && m_is_exclusive_fullscreen)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||
{
|
||||
DEV_LOG("using fs rr: {} {}", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
|
||||
return static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||
}
|
||||
}
|
||||
|
||||
return GPUDevice::GetHostRefreshRate();
|
||||
}
|
||||
|
||||
void D3D12Device::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||
{
|
||||
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||
if (m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
const u32 old_buffer_count = GetSwapChainBufferCount();
|
||||
m_vsync_enabled = enabled;
|
||||
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||
m_vsync_mode = mode;
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
@ -1181,8 +1159,8 @@ void D3D12Device::SubmitPresent()
|
||||
DebugAssert(m_swap_chain);
|
||||
|
||||
// DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it.
|
||||
if (m_vsync_enabled)
|
||||
m_swap_chain->Present(BoolToUInt32(1), 0);
|
||||
if (IsVSyncModeBlocking())
|
||||
m_swap_chain->Present(1, 0);
|
||||
else if (m_using_allow_tearing) // Disabled or VRR, VRR requires the allow tearing flag :/
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
else
|
||||
|
||||
@ -118,8 +118,7 @@ public:
|
||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||
|
||||
std::optional<float> GetHostRefreshRate() override;
|
||||
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||
void SetVSyncMode(GPUVSyncMode mode) override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
@ -276,11 +276,10 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs)
|
||||
}
|
||||
|
||||
bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version,
|
||||
bool debug_device, bool vsync, bool vsync_prefer_triple_buffer, bool threaded_presentation,
|
||||
bool debug_device, GPUVSyncMode vsync, bool threaded_presentation,
|
||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)
|
||||
{
|
||||
m_vsync_enabled = vsync;
|
||||
m_vsync_prefer_triple_buffer = vsync_prefer_triple_buffer;
|
||||
m_vsync_mode = vsync;
|
||||
m_debug_device = debug_device;
|
||||
|
||||
if (!AcquireWindow(true))
|
||||
@ -591,11 +590,6 @@ void GPUDevice::RenderImGui()
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||
{
|
||||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
void GPUDevice::UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex)
|
||||
{
|
||||
void* map;
|
||||
@ -1053,14 +1047,6 @@ void GPUDevice::ThrottlePresentation()
|
||||
Common::Timer::SleepUntil(m_last_frame_displayed_time, false);
|
||||
}
|
||||
|
||||
std::optional<float> GPUDevice::GetHostRefreshRate()
|
||||
{
|
||||
if (m_window_info.surface_refresh_rate > 0.0f)
|
||||
return m_window_info.surface_refresh_rate;
|
||||
|
||||
return WindowInfo::QueryRefreshRateForWindow(m_window_info);
|
||||
}
|
||||
|
||||
bool GPUDevice::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
return false;
|
||||
|
||||
@ -36,6 +36,14 @@ enum class RenderAPI : u32
|
||||
Metal
|
||||
};
|
||||
|
||||
enum class GPUVSyncMode : u8
|
||||
{
|
||||
Disabled,
|
||||
DoubleBuffered,
|
||||
TripleBuffered,
|
||||
Count
|
||||
};
|
||||
|
||||
class GPUSampler
|
||||
{
|
||||
public:
|
||||
@ -573,8 +581,8 @@ public:
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
|
||||
bool Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device,
|
||||
bool vsync, bool vsync_prefer_triple_buffer, bool threaded_presentation,
|
||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error);
|
||||
GPUVSyncMode vsync, bool threaded_presentation, std::optional<bool> exclusive_fullscreen_control,
|
||||
FeatureMask disabled_features, Error* error);
|
||||
void Destroy();
|
||||
|
||||
virtual bool HasSurface() const = 0;
|
||||
@ -672,8 +680,9 @@ public:
|
||||
/// Renders ImGui screen elements. Call before EndPresent().
|
||||
void RenderImGui();
|
||||
|
||||
ALWAYS_INLINE bool IsVSyncEnabled() const { return m_vsync_enabled; }
|
||||
virtual void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer);
|
||||
ALWAYS_INLINE GPUVSyncMode GetVSyncMode() const { return m_vsync_mode; }
|
||||
ALWAYS_INLINE bool IsVSyncModeBlocking() const { return (m_vsync_mode >= GPUVSyncMode::DoubleBuffered); }
|
||||
virtual void SetVSyncMode(GPUVSyncMode mode) = 0;
|
||||
|
||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||
@ -689,8 +698,6 @@ public:
|
||||
|
||||
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
|
||||
|
||||
virtual std::optional<float> GetHostRefreshRate();
|
||||
|
||||
/// Enables/disables GPU frame timing.
|
||||
virtual bool SetGPUTimingEnabled(bool enabled);
|
||||
|
||||
@ -793,8 +800,7 @@ private:
|
||||
protected:
|
||||
static Statistics s_stats;
|
||||
|
||||
bool m_vsync_enabled = false;
|
||||
bool m_vsync_prefer_triple_buffer = false;
|
||||
GPUVSyncMode m_vsync_mode = GPUVSyncMode::Disabled;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_debug_device = false;
|
||||
};
|
||||
|
||||
@ -260,12 +260,10 @@ public:
|
||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||
|
||||
std::optional<float> GetHostRefreshRate() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||
void SetVSyncMode(GPUVSyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent(bool explicit_submit) override;
|
||||
|
||||
@ -115,20 +115,14 @@ bool MetalDevice::HasSurface() const
|
||||
return (m_layer != nil);
|
||||
}
|
||||
|
||||
std::optional<float> MetalDevice::GetHostRefreshRate()
|
||||
void MetalDevice::SetVSyncMode(GPUVSyncMode mode)
|
||||
{
|
||||
return GPUDevice::GetHostRefreshRate();
|
||||
}
|
||||
|
||||
void MetalDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||
{
|
||||
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||
if (m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
m_vsync_enabled = enabled;
|
||||
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||
m_vsync_mode = mode;
|
||||
if (m_layer != nil)
|
||||
[m_layer setDisplaySyncEnabled:enabled];
|
||||
[m_layer setDisplaySyncEnabled:m_vsync_mode >= GPUVSyncMode::DoubleBuffered];
|
||||
}
|
||||
|
||||
bool MetalDevice::CreateDevice(std::string_view adapter, bool threaded_presentation,
|
||||
@ -402,7 +396,7 @@ bool MetalDevice::CreateLayer()
|
||||
}
|
||||
});
|
||||
|
||||
[m_layer setDisplaySyncEnabled:m_vsync_enabled];
|
||||
[m_layer setDisplaySyncEnabled:m_vsync_mode >= GPUVSyncMode::DoubleBuffered];
|
||||
|
||||
DebugAssert(m_layer_pass_desc == nil);
|
||||
m_layer_pass_desc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
|
||||
|
||||
@ -238,13 +238,12 @@ void OpenGLDevice::InsertDebugMessage(const char* msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||
void OpenGLDevice::SetVSyncMode(GPUVSyncMode mode)
|
||||
{
|
||||
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||
if (m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
m_vsync_enabled = enabled;
|
||||
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||
m_vsync_mode = mode;
|
||||
SetSwapInterval();
|
||||
}
|
||||
|
||||
@ -584,7 +583,7 @@ void OpenGLDevice::SetSwapInterval()
|
||||
return;
|
||||
|
||||
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||
const s32 interval = m_vsync_enabled ? 1 : 0;
|
||||
const s32 interval = (m_vsync_mode >= GPUVSyncMode::DoubleBuffered) ? 1 : 0;
|
||||
GLint current_fbo = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
@ -100,7 +100,7 @@ public:
|
||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||
|
||||
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||
void SetVSyncMode(GPUVSyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent(bool explicit_present) override;
|
||||
|
||||
@ -23,4 +23,10 @@ bool PlaySoundAsync(const char* path);
|
||||
namespace Host {
|
||||
/// Return the current window handle. Needed for DInput.
|
||||
std::optional<WindowInfo> GetTopLevelWindowInfo();
|
||||
} // namespace Host
|
||||
} // namespace Host
|
||||
|
||||
// TODO: Move all the other Cocoa stuff in here.
|
||||
namespace CocoaTools {
|
||||
/// Returns the refresh rate of the display the window is placed on.
|
||||
std::optional<float> GetViewRefreshRate(const WindowInfo& wi);
|
||||
}
|
||||
|
||||
@ -147,3 +147,24 @@ void CocoaTools::DestroyMetalLayer(WindowInfo* wi)
|
||||
[view setWantsLayer:NO];
|
||||
[layer release];
|
||||
}
|
||||
|
||||
std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
|
||||
{
|
||||
if (![NSThread isMainThread])
|
||||
{
|
||||
std::optional<float> ret;
|
||||
dispatch_sync(dispatch_get_main_queue(), [&ret, wi]{ ret = GetViewRefreshRate(wi); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<float> ret;
|
||||
NSView* const view = (__bridge NSView*)wi.window_handle;
|
||||
const u32 did = [[[[[view window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
|
||||
{
|
||||
ret = CGDisplayModeGetRefreshRate(mode);
|
||||
CGDisplayModeRelease(mode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -440,8 +440,13 @@ VkPresentModeKHR VulkanDevice::SelectPresentMode() const
|
||||
{
|
||||
// Use mailbox/triple buffering for "normal" vsync, due to PAL refresh rate mismatch.
|
||||
// Otherwise, use FIFO when syncing to host, because we don't want to return early.
|
||||
return m_vsync_enabled ? (m_vsync_prefer_triple_buffer ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_FIFO_KHR) :
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
static constexpr std::array<VkPresentModeKHR, static_cast<size_t>(GPUVSyncMode::Count)> modes = {{
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR, // Disabled
|
||||
VK_PRESENT_MODE_FIFO_KHR, // DoubleBuffered
|
||||
VK_PRESENT_MODE_MAILBOX_KHR, // TripleBuffered
|
||||
}};
|
||||
|
||||
return modes[static_cast<size_t>(m_vsync_mode)];
|
||||
}
|
||||
|
||||
bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
|
||||
@ -2344,13 +2349,12 @@ std::string VulkanDevice::GetDriverInfo() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VulkanDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||
void VulkanDevice::SetVSyncMode(GPUVSyncMode mode)
|
||||
{
|
||||
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||
if (m_vsync_mode == mode)
|
||||
return;
|
||||
|
||||
m_vsync_enabled = enabled;
|
||||
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||
m_vsync_mode = mode;
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
|
||||
@ -129,7 +129,7 @@ public:
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||
void SetVSyncMode(GPUVSyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent(bool explicit_present) override;
|
||||
|
||||
@ -160,6 +160,18 @@ std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include "util/platform_misc.h"
|
||||
|
||||
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
||||
{
|
||||
if (wi.type == WindowInfo::Type::MacOS)
|
||||
return CocoaTools::GetViewRefreshRate(wi);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef ENABLE_X11
|
||||
|
||||
Reference in New Issue
Block a user