GPUDevice: Make vsync actually tear-free
Apparently users prefer stutter over tearing...
This commit is contained in:
parent
dd98b630ea
commit
6cad97b404
|
@ -288,7 +288,8 @@ bool Host::CreateGPUDevice(RenderAPI api, Error* error)
|
||||||
if (!g_gpu_device || !g_gpu_device->Create(
|
if (!g_gpu_device || !g_gpu_device->Create(
|
||||||
g_settings.gpu_adapter,
|
g_settings.gpu_adapter,
|
||||||
g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
||||||
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::IsVSyncEffectivelyEnabled(),
|
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::IsHostVSyncEffectivelyEnabled(),
|
||||||
|
System::IsHostVSyncEffectivelyEnabled() && !System::IsHostVSyncUsedForTiming(),
|
||||||
g_settings.gpu_threaded_presentation, exclusive_fullscreen_control,
|
g_settings.gpu_threaded_presentation, exclusive_fullscreen_control,
|
||||||
static_cast<GPUDevice::FeatureMask>(disabled_features), &create_error))
|
static_cast<GPUDevice::FeatureMask>(disabled_features), &create_error))
|
||||||
{
|
{
|
||||||
|
@ -331,9 +332,15 @@ void Host::UpdateDisplayWindow()
|
||||||
|
|
||||||
ImGuiManager::WindowResized();
|
ImGuiManager::WindowResized();
|
||||||
|
|
||||||
// If we're paused, re-present the current frame at the new window size.
|
if (System::IsValid())
|
||||||
if (System::IsValid() && System::IsPaused())
|
{
|
||||||
System::InvalidateDisplay();
|
// Fix up vsync etc.
|
||||||
|
System::UpdateSpeedLimiterState();
|
||||||
|
|
||||||
|
// If we're paused, re-present the current frame at the new window size.
|
||||||
|
if (System::IsPaused())
|
||||||
|
System::InvalidateDisplay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
|
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
|
||||||
|
|
|
@ -2835,7 +2835,6 @@ void System::UpdateSpeedLimiterState()
|
||||||
{
|
{
|
||||||
DebugAssert(IsValid());
|
DebugAssert(IsValid());
|
||||||
|
|
||||||
const float old_target_speed = s_target_speed;
|
|
||||||
s_target_speed = s_turbo_enabled ?
|
s_target_speed = s_turbo_enabled ?
|
||||||
g_settings.turbo_speed :
|
g_settings.turbo_speed :
|
||||||
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
|
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
|
||||||
|
@ -2862,7 +2861,7 @@ void System::UpdateSpeedLimiterState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// When syncing to host and using vsync, we don't need to sleep.
|
// When syncing to host and using vsync, we don't need to sleep.
|
||||||
s_syncing_to_host_with_vsync = (s_syncing_to_host && IsVSyncEffectivelyEnabled());
|
s_syncing_to_host_with_vsync = (s_syncing_to_host && IsHostVSyncEffectivelyEnabled());
|
||||||
if (s_syncing_to_host_with_vsync)
|
if (s_syncing_to_host_with_vsync)
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("Using host vsync for throttling.");
|
Log_InfoPrintf("Using host vsync for throttling.");
|
||||||
|
@ -2886,22 +2885,27 @@ void System::UpdateSpeedLimiterState()
|
||||||
|
|
||||||
void System::UpdateDisplaySync()
|
void System::UpdateDisplaySync()
|
||||||
{
|
{
|
||||||
const bool vsync_enabled = IsVSyncEffectivelyEnabled();
|
const bool vsync_enabled = IsHostVSyncEffectivelyEnabled();
|
||||||
const bool syncing_to_host_vsync = (s_syncing_to_host && vsync_enabled);
|
|
||||||
const float max_display_fps = (s_throttler_enabled || s_syncing_to_host) ? 0.0f : g_settings.display_max_fps;
|
const float max_display_fps = (s_throttler_enabled || s_syncing_to_host) ? 0.0f : g_settings.display_max_fps;
|
||||||
Log_VerboseFmt("VSync: {}{}", vsync_enabled ? "Enabled" : "Disabled",
|
Log_VerboseFmt("VSync: {}{}", vsync_enabled ? "Enabled" : "Disabled",
|
||||||
syncing_to_host_vsync ? " (for throttling)" : "");
|
s_syncing_to_host_with_vsync ? " (for throttling)" : "");
|
||||||
Log_VerboseFmt("Max display fps: {}", max_display_fps);
|
Log_VerboseFmt("Max display fps: {}", max_display_fps);
|
||||||
Log_VerboseFmt("Preset timing: {}", s_optimal_frame_pacing ? "consistent" : "immediate");
|
Log_VerboseFmt("Preset timing: {}", s_optimal_frame_pacing ? "consistent" : "immediate");
|
||||||
|
|
||||||
g_gpu_device->SetDisplayMaxFPS(max_display_fps);
|
g_gpu_device->SetDisplayMaxFPS(max_display_fps);
|
||||||
g_gpu_device->SetVSyncEnabled(vsync_enabled);
|
g_gpu_device->SetVSyncEnabled(vsync_enabled, vsync_enabled && !IsHostVSyncUsedForTiming());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsVSyncEffectivelyEnabled()
|
bool System::IsHostVSyncEffectivelyEnabled()
|
||||||
{
|
{
|
||||||
// Disable vsync if running outside 100%.
|
// Disable vsync if running outside 100%.
|
||||||
return (g_settings.display_vsync && IsValid() && !IsRunningAtNonStandardSpeed());
|
return (g_settings.display_vsync && (s_state != State::Shutdown && s_state != State::Stopping) &&
|
||||||
|
!IsRunningAtNonStandardSpeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::IsHostVSyncUsedForTiming()
|
||||||
|
{
|
||||||
|
return (IsHostVSyncEffectivelyEnabled() && s_syncing_to_host_with_vsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsFastForwardEnabled()
|
bool System::IsFastForwardEnabled()
|
||||||
|
@ -4505,7 +4509,7 @@ bool System::IsRunningAtNonStandardSpeed()
|
||||||
if (!IsValid())
|
if (!IsValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const float target_speed = System::GetTargetSpeed();
|
const float target_speed = GetTargetSpeed();
|
||||||
return (target_speed <= 0.95f || target_speed >= 1.05f);
|
return (target_speed <= 0.95f || target_speed >= 1.05f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -466,7 +466,10 @@ void ToggleWidescreen();
|
||||||
bool IsRunningAtNonStandardSpeed();
|
bool IsRunningAtNonStandardSpeed();
|
||||||
|
|
||||||
/// Returns true if vsync should be used.
|
/// Returns true if vsync should be used.
|
||||||
bool IsVSyncEffectivelyEnabled();
|
bool IsHostVSyncEffectivelyEnabled();
|
||||||
|
|
||||||
|
/// Returns true if vsync is being used for frame pacing.
|
||||||
|
bool IsHostVSyncUsedForTiming();
|
||||||
|
|
||||||
/// Quick switch between software and hardware rendering.
|
/// Quick switch between software and hardware rendering.
|
||||||
void ToggleSoftwareRendering();
|
void ToggleSoftwareRendering();
|
||||||
|
|
|
@ -197,6 +197,13 @@ void D3D11Device::SetFeatures(FeatureMask disabled_features)
|
||||||
m_features.prefer_unused_textures = false;
|
m_features.prefer_unused_textures = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D11Device::CreateSwapChain()
|
bool D3D11Device::CreateSwapChain()
|
||||||
{
|
{
|
||||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||||
|
@ -233,7 +240,7 @@ bool D3D11Device::CreateSwapChain()
|
||||||
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||||
swap_chain_desc.Format = dxgi_format;
|
swap_chain_desc.Format = dxgi_format;
|
||||||
swap_chain_desc.SampleDesc.Count = 1;
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
swap_chain_desc.BufferCount = 3;
|
swap_chain_desc.BufferCount = GetSwapChainBufferCount();
|
||||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
||||||
|
|
||||||
|
@ -593,8 +600,7 @@ std::optional<float> D3D11Device::GetHostRefreshRate()
|
||||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||||
desc.BufferDesc.RefreshRate.Denominator > 0)
|
desc.BufferDesc.RefreshRate.Denominator > 0)
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator,
|
Log_DevFmt("using fs rr: {} {}", desc.BufferDesc.RefreshRate.Numerator, desc.BufferDesc.RefreshRate.Denominator);
|
||||||
desc.BufferDesc.RefreshRate.Denominator);
|
|
||||||
return static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
return static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
}
|
}
|
||||||
|
@ -603,6 +609,25 @@ std::optional<float> D3D11Device::GetHostRefreshRate()
|
||||||
return GPUDevice::GetHostRefreshRate();
|
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)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const u32 old_buffer_count = GetSwapChainBufferCount();
|
||||||
|
m_vsync_enabled = enabled;
|
||||||
|
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (GetSwapChainBufferCount() != old_buffer_count)
|
||||||
|
{
|
||||||
|
DestroySwapChain();
|
||||||
|
if (!CreateSwapChain())
|
||||||
|
Panic("Failed to recreate swap chain after vsync change.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D11Device::BeginPresent(bool skip_present)
|
bool D3D11Device::BeginPresent(bool skip_present)
|
||||||
{
|
{
|
||||||
if (skip_present)
|
if (skip_present)
|
||||||
|
|
|
@ -97,6 +97,7 @@ public:
|
||||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
std::optional<float> GetHostRefreshRate() override;
|
std::optional<float> GetHostRefreshRate() override;
|
||||||
|
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
@ -135,6 +136,7 @@ private:
|
||||||
|
|
||||||
void SetFeatures(FeatureMask disabled_features);
|
void SetFeatures(FeatureMask disabled_features);
|
||||||
|
|
||||||
|
u32 GetSwapChainBufferCount() const;
|
||||||
bool CreateSwapChain();
|
bool CreateSwapChain();
|
||||||
bool CreateSwapChainRTV();
|
bool CreateSwapChainRTV();
|
||||||
void DestroySwapChain();
|
void DestroySwapChain();
|
||||||
|
|
|
@ -797,6 +797,13 @@ bool D3D12Device::HasSurface() const
|
||||||
return static_cast<bool>(m_swap_chain);
|
return static_cast<bool>(m_swap_chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D12Device::CreateSwapChain()
|
bool D3D12Device::CreateSwapChain()
|
||||||
{
|
{
|
||||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||||
|
@ -830,7 +837,7 @@ bool D3D12Device::CreateSwapChain()
|
||||||
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||||
swap_chain_desc.Format = fm.resource_format;
|
swap_chain_desc.Format = fm.resource_format;
|
||||||
swap_chain_desc.SampleDesc.Count = 1;
|
swap_chain_desc.SampleDesc.Count = 1;
|
||||||
swap_chain_desc.BufferCount = 3;
|
swap_chain_desc.BufferCount = GetSwapChainBufferCount();
|
||||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||||
|
|
||||||
|
@ -1081,6 +1088,42 @@ std::string D3D12Device::GetDriverInfo() const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<float> D3D12Device::GetHostRefreshRate()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Log_DevFmt("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)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const u32 old_buffer_count = GetSwapChainBufferCount();
|
||||||
|
m_vsync_enabled = enabled;
|
||||||
|
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||||
|
if (!m_swap_chain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (GetSwapChainBufferCount() != old_buffer_count)
|
||||||
|
{
|
||||||
|
DestroySwapChain();
|
||||||
|
if (!CreateSwapChain())
|
||||||
|
Panic("Failed to recreate swap chain after vsync change.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool D3D12Device::BeginPresent(bool frame_skip)
|
bool D3D12Device::BeginPresent(bool frame_skip)
|
||||||
{
|
{
|
||||||
if (InRenderPass())
|
if (InRenderPass())
|
||||||
|
|
|
@ -118,6 +118,9 @@ public:
|
||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
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 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;
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
|
@ -220,6 +223,7 @@ private:
|
||||||
|
|
||||||
void SetFeatures(FeatureMask disabled_features);
|
void SetFeatures(FeatureMask disabled_features);
|
||||||
|
|
||||||
|
u32 GetSwapChainBufferCount() const;
|
||||||
bool CreateSwapChain();
|
bool CreateSwapChain();
|
||||||
bool CreateSwapChainRTV();
|
bool CreateSwapChainRTV();
|
||||||
void DestroySwapChainRTVs();
|
void DestroySwapChainRTVs();
|
||||||
|
|
|
@ -276,10 +276,11 @@ 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 GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version,
|
||||||
bool debug_device, bool vsync, bool threaded_presentation,
|
bool debug_device, bool vsync, bool vsync_prefer_triple_buffer, bool threaded_presentation,
|
||||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)
|
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)
|
||||||
{
|
{
|
||||||
m_vsync_enabled = vsync;
|
m_vsync_enabled = vsync;
|
||||||
|
m_vsync_prefer_triple_buffer = vsync_prefer_triple_buffer;
|
||||||
m_debug_device = debug_device;
|
m_debug_device = debug_device;
|
||||||
|
|
||||||
if (!AcquireWindow(true))
|
if (!AcquireWindow(true))
|
||||||
|
@ -590,7 +591,7 @@ void GPUDevice::RenderImGui()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUDevice::SetVSyncEnabled(bool enabled)
|
void GPUDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||||
{
|
{
|
||||||
m_vsync_enabled = enabled;
|
m_vsync_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -573,8 +573,8 @@ public:
|
||||||
virtual RenderAPI GetRenderAPI() const = 0;
|
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 Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device,
|
||||||
bool vsync, bool threaded_presentation, std::optional<bool> exclusive_fullscreen_control,
|
bool vsync, bool vsync_prefer_triple_buffer, bool threaded_presentation,
|
||||||
FeatureMask disabled_features, Error* error);
|
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error);
|
||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
||||||
virtual bool HasSurface() const = 0;
|
virtual bool HasSurface() const = 0;
|
||||||
|
@ -673,7 +673,7 @@ public:
|
||||||
void RenderImGui();
|
void RenderImGui();
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsVSyncEnabled() const { return m_vsync_enabled; }
|
ALWAYS_INLINE bool IsVSyncEnabled() const { return m_vsync_enabled; }
|
||||||
virtual void SetVSyncEnabled(bool enabled);
|
virtual void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer);
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||||
|
@ -794,6 +794,7 @@ protected:
|
||||||
static Statistics s_stats;
|
static Statistics s_stats;
|
||||||
|
|
||||||
bool m_vsync_enabled = false;
|
bool m_vsync_enabled = false;
|
||||||
|
bool m_vsync_prefer_triple_buffer = false;
|
||||||
bool m_gpu_timing_enabled = false;
|
bool m_gpu_timing_enabled = false;
|
||||||
bool m_debug_device = false;
|
bool m_debug_device = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -265,7 +265,7 @@ public:
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
void SetVSyncEnabled(bool enabled) override;
|
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||||
|
|
||||||
bool BeginPresent(bool skip_present) override;
|
bool BeginPresent(bool skip_present) override;
|
||||||
void EndPresent(bool explicit_submit) override;
|
void EndPresent(bool explicit_submit) override;
|
||||||
|
|
|
@ -124,12 +124,13 @@ std::optional<float> MetalDevice::GetHostRefreshRate()
|
||||||
return GPUDevice::GetHostRefreshRate();
|
return GPUDevice::GetHostRefreshRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::SetVSyncEnabled(bool enabled)
|
void MetalDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||||
{
|
{
|
||||||
if (m_vsync_enabled == enabled)
|
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_vsync_enabled = enabled;
|
m_vsync_enabled = enabled;
|
||||||
|
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||||
if (m_layer != nil)
|
if (m_layer != nil)
|
||||||
[m_layer setDisplaySyncEnabled:enabled];
|
[m_layer setDisplaySyncEnabled:enabled];
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,12 +238,13 @@ void OpenGLDevice::InsertDebugMessage(const char* msg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLDevice::SetVSyncEnabled(bool enabled)
|
void OpenGLDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||||
{
|
{
|
||||||
if (m_vsync_enabled == enabled)
|
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_vsync_enabled = enabled;
|
m_vsync_enabled = enabled;
|
||||||
|
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||||
SetSwapInterval();
|
SetSwapInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,7 +584,7 @@ void OpenGLDevice::SetSwapInterval()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Window framebuffer has to be bound to call SetSwapInterval.
|
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||||
const s32 interval = m_vsync_enabled ? (m_gl_context->SupportsNegativeSwapInterval() ? -1 : 1) : 0;
|
const s32 interval = m_vsync_enabled ? 1 : 0;
|
||||||
GLint current_fbo = 0;
|
GLint current_fbo = 0;
|
||||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
|
|
@ -100,7 +100,7 @@ public:
|
||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
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 DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
void SetVSyncEnabled(bool enabled) override;
|
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||||
|
|
||||||
bool BeginPresent(bool skip_present) override;
|
bool BeginPresent(bool skip_present) override;
|
||||||
void EndPresent(bool explicit_present) override;
|
void EndPresent(bool explicit_present) override;
|
||||||
|
|
|
@ -436,6 +436,14 @@ bool VulkanDevice::SelectDeviceFeatures()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
|
bool VulkanDevice::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
|
||||||
{
|
{
|
||||||
u32 queue_family_count;
|
u32 queue_family_count;
|
||||||
|
@ -2041,7 +2049,7 @@ bool VulkanDevice::CreateDevice(std::string_view adapter, bool threaded_presenta
|
||||||
|
|
||||||
if (surface != VK_NULL_HANDLE)
|
if (surface != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control);
|
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, SelectPresentMode(), m_exclusive_fullscreen_control);
|
||||||
if (!m_swap_chain)
|
if (!m_swap_chain)
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to create swap chain");
|
Error::SetStringView(error, "Failed to create swap chain");
|
||||||
|
@ -2262,7 +2270,7 @@ bool VulkanDevice::UpdateWindow()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control);
|
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, SelectPresentMode(), m_exclusive_fullscreen_control);
|
||||||
if (!m_swap_chain)
|
if (!m_swap_chain)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to create swap chain");
|
Log_ErrorPrintf("Failed to create swap chain");
|
||||||
|
@ -2338,25 +2346,22 @@ std::string VulkanDevice::GetDriverInfo() const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDevice::SetVSyncEnabled(bool enabled)
|
void VulkanDevice::SetVSyncEnabled(bool enabled, bool prefer_triple_buffer)
|
||||||
{
|
{
|
||||||
if (m_vsync_enabled == enabled)
|
if (m_vsync_enabled == enabled && m_vsync_prefer_triple_buffer == prefer_triple_buffer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_vsync_enabled = enabled;
|
m_vsync_enabled = enabled;
|
||||||
|
m_vsync_prefer_triple_buffer = prefer_triple_buffer;
|
||||||
if (!m_swap_chain)
|
if (!m_swap_chain)
|
||||||
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->SetVSyncEnabled(enabled))
|
if (!m_swap_chain->SetRequestedPresentMode(SelectPresentMode()))
|
||||||
{
|
{
|
||||||
// Try switching back to the old mode..
|
Panic("Failed to update swap chain present mode.");
|
||||||
if (!m_swap_chain->SetVSyncEnabled(!enabled))
|
m_swap_chain.reset();
|
||||||
{
|
|
||||||
Panic("Failed to reset old vsync mode after failure");
|
|
||||||
m_swap_chain.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2390,6 +2395,7 @@ bool VulkanDevice::BeginPresent(bool frame_skip)
|
||||||
VkResult res = m_swap_chain->AcquireNextImage();
|
VkResult res = m_swap_chain->AcquireNextImage();
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||||
m_swap_chain->ReleaseCurrentImage();
|
m_swap_chain->ReleaseCurrentImage();
|
||||||
|
|
||||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
|
@ -2416,7 +2422,6 @@ bool VulkanDevice::BeginPresent(bool frame_skip)
|
||||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||||
{
|
{
|
||||||
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
|
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
|
||||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
|
||||||
SubmitCommandBuffer(false);
|
SubmitCommandBuffer(false);
|
||||||
TrimTexturePool();
|
TrimTexturePool();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -129,7 +129,7 @@ public:
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
||||||
void SetVSyncEnabled(bool enabled) override;
|
void SetVSyncEnabled(bool enabled, bool prefer_triple_buffer) override;
|
||||||
|
|
||||||
bool BeginPresent(bool skip_present) override;
|
bool BeginPresent(bool skip_present) override;
|
||||||
void EndPresent(bool explicit_present) override;
|
void EndPresent(bool explicit_present) override;
|
||||||
|
@ -324,6 +324,7 @@ private:
|
||||||
bool enable_debug_utils);
|
bool enable_debug_utils);
|
||||||
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
||||||
bool SelectDeviceFeatures();
|
bool SelectDeviceFeatures();
|
||||||
|
VkPresentModeKHR SelectPresentMode() const;
|
||||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
||||||
void ProcessDeviceExtensions();
|
void ProcessDeviceExtensions();
|
||||||
|
|
||||||
|
|
|
@ -70,10 +70,10 @@ static const char* PresentModeToString(VkPresentModeKHR mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR present_mode,
|
||||||
std::optional<bool> exclusive_fullscreen_control)
|
std::optional<bool> exclusive_fullscreen_control)
|
||||||
: m_window_info(wi), m_surface(surface), m_exclusive_fullscreen_control(exclusive_fullscreen_control),
|
: m_window_info(wi), m_surface(surface), m_requested_present_mode(present_mode),
|
||||||
m_vsync_enabled(vsync)
|
m_exclusive_fullscreen_control(exclusive_fullscreen_control)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,11 +208,12 @@ void VulkanSwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<VulkanSwapChain> VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
std::unique_ptr<VulkanSwapChain> VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
||||||
|
VkPresentModeKHR present_mode,
|
||||||
std::optional<bool> exclusive_fullscreen_control)
|
std::optional<bool> exclusive_fullscreen_control)
|
||||||
{
|
{
|
||||||
std::unique_ptr<VulkanSwapChain> swap_chain =
|
std::unique_ptr<VulkanSwapChain> swap_chain =
|
||||||
std::unique_ptr<VulkanSwapChain>(new VulkanSwapChain(wi, surface, vsync, exclusive_fullscreen_control));
|
std::unique_ptr<VulkanSwapChain>(new VulkanSwapChain(wi, surface, present_mode, exclusive_fullscreen_control));
|
||||||
if (!swap_chain->CreateSwapChain())
|
if (!swap_chain->CreateSwapChain())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -252,9 +253,9 @@ std::optional<VkSurfaceFormatKHR> VulkanSwapChain::SelectSurfaceFormat(VkSurface
|
||||||
return VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
|
return VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_ErrorPrintf("Failed to find a suitable format for swap chain buffers. Available formats were:");
|
Log_ErrorPrint("Failed to find a suitable format for swap chain buffers. Available formats were:");
|
||||||
for (const VkSurfaceFormatKHR& sf : surface_formats)
|
for (const VkSurfaceFormatKHR& sf : surface_formats)
|
||||||
Log_ErrorPrintf(" %u", static_cast<unsigned>(sf.format));
|
Log_ErrorFmt(" {}", static_cast<unsigned>(sf.format));
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -290,25 +291,19 @@ std::optional<VkPresentModeKHR> VulkanSwapChain::SelectPresentMode(VkSurfaceKHR
|
||||||
{
|
{
|
||||||
selected_mode = requested_mode;
|
selected_mode = requested_mode;
|
||||||
}
|
}
|
||||||
else if (requested_mode != VK_PRESENT_MODE_FIFO_KHR && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
else if (requested_mode == VK_PRESENT_MODE_IMMEDIATE_KHR && 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 vsync-off, since we don't want to block.
|
||||||
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))
|
|
||||||
{
|
|
||||||
// Fallback to FIFO if we're using any kind of vsync.
|
|
||||||
// This should never fail, FIFO is mandated.
|
|
||||||
selected_mode = VK_PRESENT_MODE_FIFO_KHR;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fall back to whatever is available.
|
// Fallback to FIFO if we we can't use mailbox. This should never fail, FIFO is mandated.
|
||||||
selected_mode = present_modes[0];
|
selected_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_DevPrintf("(SwapChain) Preferred present mode: %s, selected: %s", PresentModeToString(requested_mode),
|
Log_DevFmt("Preferred present mode: {}, selected: {}", PresentModeToString(requested_mode),
|
||||||
PresentModeToString(selected_mode));
|
PresentModeToString(selected_mode));
|
||||||
|
|
||||||
return selected_mode;
|
return selected_mode;
|
||||||
}
|
}
|
||||||
|
@ -319,11 +314,7 @@ bool VulkanSwapChain::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_requested_present_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;
|
||||||
|
|
||||||
|
@ -337,12 +328,12 @@ bool VulkanSwapChain::CreateSwapChain()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select number of images in swap chain, we prefer one buffer in the background to work on
|
// Select number of images in swap chain, we prefer one buffer in the background to work on in triple-buffered mode.
|
||||||
u32 image_count = std::max(surface_capabilities.minImageCount + 1u, 2u);
|
|
||||||
|
|
||||||
// maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers.
|
// maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers.
|
||||||
if (surface_capabilities.maxImageCount > 0)
|
u32 image_count = std::clamp<u32>(
|
||||||
image_count = std::min(image_count, surface_capabilities.maxImageCount);
|
(m_requested_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) ? 3 : 2, surface_capabilities.minImageCount,
|
||||||
|
(surface_capabilities.maxImageCount == 0) ? std::numeric_limits<u32>::max() : surface_capabilities.maxImageCount);
|
||||||
|
Log_DevFmt("Creating a swap chain with {} images", image_count);
|
||||||
|
|
||||||
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
|
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
|
||||||
// determines window size? Android sometimes lags updating currentExtent, so don't use it.
|
// determines window size? Android sometimes lags updating currentExtent, so don't use it.
|
||||||
|
@ -376,7 +367,7 @@ bool VulkanSwapChain::CreateSwapChain()
|
||||||
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
if ((surface_capabilities.supportedUsageFlags & image_usage) != image_usage)
|
if ((surface_capabilities.supportedUsageFlags & image_usage) != image_usage)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Vulkan: Swap chain does not support usage as color attachment");
|
Log_ErrorPrint("Vulkan: Swap chain does not support usage as color attachment");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,19 +422,19 @@ bool VulkanSwapChain::CreateSwapChain()
|
||||||
exclusive_win32_info.hmonitor =
|
exclusive_win32_info.hmonitor =
|
||||||
MonitorFromWindow(reinterpret_cast<HWND>(m_window_info.window_handle), MONITOR_DEFAULTTONEAREST);
|
MonitorFromWindow(reinterpret_cast<HWND>(m_window_info.window_handle), MONITOR_DEFAULTTONEAREST);
|
||||||
if (!exclusive_win32_info.hmonitor)
|
if (!exclusive_win32_info.hmonitor)
|
||||||
Log_ErrorPrintf("MonitorFromWindow() for exclusive fullscreen exclusive override failed.");
|
Log_ErrorPrint("MonitorFromWindow() for exclusive fullscreen exclusive override failed.");
|
||||||
|
|
||||||
Vulkan::AddPointerToChain(&swap_chain_info, &exclusive_info);
|
Vulkan::AddPointerToChain(&swap_chain_info, &exclusive_info);
|
||||||
Vulkan::AddPointerToChain(&swap_chain_info, &exclusive_win32_info);
|
Vulkan::AddPointerToChain(&swap_chain_info, &exclusive_win32_info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Exclusive fullscreen control requested, but VK_EXT_full_screen_exclusive is not supported.");
|
Log_ErrorPrint("Exclusive fullscreen control requested, but VK_EXT_full_screen_exclusive is not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (m_exclusive_fullscreen_control.has_value())
|
if (m_exclusive_fullscreen_control.has_value())
|
||||||
Log_ErrorPrintf("Exclusive fullscreen control requested, but is not supported on this platform.");
|
Log_ErrorPrint("Exclusive fullscreen control requested, but is not supported on this platform.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
res = vkCreateSwapchainKHR(dev.GetVulkanDevice(), &swap_chain_info, nullptr, &m_swap_chain);
|
res = vkCreateSwapchainKHR(dev.GetVulkanDevice(), &swap_chain_info, nullptr, &m_swap_chain);
|
||||||
|
@ -465,7 +456,7 @@ bool VulkanSwapChain::CreateSwapChain()
|
||||||
m_actual_present_mode = present_mode.value();
|
m_actual_present_mode = present_mode.value();
|
||||||
if (m_window_info.surface_format == GPUTexture::Format::Unknown)
|
if (m_window_info.surface_format == GPUTexture::Format::Unknown)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Unknown Vulkan surface format %u", static_cast<u32>(surface_format->format));
|
Log_ErrorFmt("Unknown Vulkan surface format {}", static_cast<u32>(surface_format->format));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,15 +624,15 @@ bool VulkanSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_s
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanSwapChain::SetVSyncEnabled(bool enabled)
|
bool VulkanSwapChain::SetRequestedPresentMode(VkPresentModeKHR mode)
|
||||||
{
|
{
|
||||||
if (m_vsync_enabled == enabled)
|
if (m_requested_present_mode == mode)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
m_vsync_enabled = enabled;
|
m_requested_present_mode = mode;
|
||||||
|
|
||||||
// Recreate the swap chain with the new present mode.
|
// Recreate the swap chain with the new present mode.
|
||||||
Log_VerbosePrintf("Recreating swap chain to change present mode.");
|
Log_VerbosePrint("Recreating swap chain to change present mode.");
|
||||||
DestroySwapChainImages();
|
DestroySwapChainImages();
|
||||||
if (!CreateSwapChain())
|
if (!CreateSwapChain())
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,8 @@ public:
|
||||||
static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface);
|
static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface);
|
||||||
|
|
||||||
// Create a new swap chain from a pre-existing surface.
|
// Create a new swap chain from a pre-existing surface.
|
||||||
static std::unique_ptr<VulkanSwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
static std::unique_ptr<VulkanSwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
||||||
|
VkPresentModeKHR present_mode,
|
||||||
std::optional<bool> exclusive_fullscreen_control);
|
std::optional<bool> exclusive_fullscreen_control);
|
||||||
|
|
||||||
ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; }
|
ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||||
|
@ -73,10 +74,10 @@ 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 SetVSyncEnabled(bool enabled);
|
bool SetRequestedPresentMode(VkPresentModeKHR mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR present_mode,
|
||||||
std::optional<bool> exclusive_fullscreen_control);
|
std::optional<bool> exclusive_fullscreen_control);
|
||||||
|
|
||||||
static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface);
|
static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface);
|
||||||
|
@ -107,15 +108,16 @@ private:
|
||||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||||
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
u32 m_current_image = 0;
|
||||||
|
u32 m_current_semaphore = 0;
|
||||||
|
|
||||||
std::vector<Image> m_images;
|
std::vector<Image> m_images;
|
||||||
std::vector<ImageSemaphores> m_semaphores;
|
std::vector<ImageSemaphores> m_semaphores;
|
||||||
|
|
||||||
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkPresentModeKHR m_requested_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||||
VkPresentModeKHR m_actual_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
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<VkResult> m_image_acquire_result;
|
||||||
std::optional<bool> m_exclusive_fullscreen_control;
|
std::optional<bool> m_exclusive_fullscreen_control;
|
||||||
bool m_vsync_enabled = false;
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue