GPUDevice: Add support for VRR and relaxed vsync
This commit is contained in:
@ -28,6 +28,7 @@ add_library(util
|
||||
gpu_shader_cache.h
|
||||
gpu_texture.cpp
|
||||
gpu_texture.h
|
||||
gpu_types.h
|
||||
host.cpp
|
||||
host.h
|
||||
http_downloader.cpp
|
||||
|
||||
@ -233,7 +233,8 @@ bool D3D11Device::CreateSwapChain()
|
||||
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;
|
||||
|
||||
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !m_is_exclusive_fullscreen);
|
||||
m_using_allow_tearing =
|
||||
(m_allow_tearing_supported && m_using_flip_model_swap_chain && !m_is_exclusive_fullscreen);
|
||||
if (m_using_allow_tearing)
|
||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
|
||||
@ -600,11 +601,6 @@ bool D3D11Device::GetHostRefreshRate(float* refresh_rate)
|
||||
return GPUDevice::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void D3D11Device::SetVSync(bool enabled)
|
||||
{
|
||||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
bool D3D11Device::BeginPresent(bool skip_present)
|
||||
{
|
||||
if (skip_present)
|
||||
@ -633,7 +629,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 ((m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed) && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
@ -650,13 +646,16 @@ void D3D11Device::EndPresent()
|
||||
{
|
||||
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||
|
||||
if (!m_vsync_enabled && m_gpu_timing_enabled)
|
||||
if (m_sync_mode != DisplaySyncMode::VSync && m_sync_mode != DisplaySyncMode::VSyncRelaxed && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
if (!m_vsync_enabled && m_using_allow_tearing)
|
||||
// DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it.
|
||||
if (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed)
|
||||
m_swap_chain->Present(BoolToUInt32(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
|
||||
m_swap_chain->Present(BoolToUInt32(m_vsync_enabled), 0);
|
||||
m_swap_chain->Present(0, 0);
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
|
||||
@ -98,8 +98,6 @@ public:
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
||||
|
||||
@ -1060,11 +1060,6 @@ std::string D3D12Device::GetDriverInfo() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void D3D12Device::SetVSync(bool enabled)
|
||||
{
|
||||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
bool D3D12Device::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (InRenderPass())
|
||||
@ -1112,10 +1107,13 @@ void D3D12Device::EndPresent()
|
||||
|
||||
SubmitCommandList(false);
|
||||
|
||||
if (!m_vsync_enabled && m_using_allow_tearing)
|
||||
// DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it.
|
||||
if (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed)
|
||||
m_swap_chain->Present(BoolToUInt32(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
|
||||
m_swap_chain->Present(static_cast<UINT>(m_vsync_enabled), 0);
|
||||
m_swap_chain->Present(0, 0);
|
||||
|
||||
TrimTexturePool();
|
||||
}
|
||||
|
||||
@ -119,8 +119,6 @@ public:
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetVSync(bool enabled) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
||||
|
||||
@ -271,10 +271,11 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs)
|
||||
}
|
||||
|
||||
bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& shader_cache_path,
|
||||
u32 shader_cache_version, bool debug_device, bool vsync, bool threaded_presentation,
|
||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)
|
||||
u32 shader_cache_version, bool debug_device, DisplaySyncMode sync_mode,
|
||||
bool threaded_presentation, std::optional<bool> exclusive_fullscreen_control,
|
||||
FeatureMask disabled_features, Error* error)
|
||||
{
|
||||
m_vsync_enabled = vsync;
|
||||
m_sync_mode = sync_mode;
|
||||
m_debug_device = debug_device;
|
||||
|
||||
if (!AcquireWindow(true))
|
||||
@ -584,6 +585,11 @@ void GPUDevice::RenderImGui()
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
m_sync_mode = mode;
|
||||
}
|
||||
|
||||
void GPUDevice::UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex)
|
||||
{
|
||||
void* map;
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include "gpu_shader_cache.h"
|
||||
#include "gpu_texture.h"
|
||||
#include "gpu_types.h"
|
||||
#include "window_info.h"
|
||||
|
||||
#include "common/bitfield.h"
|
||||
@ -25,17 +26,6 @@
|
||||
|
||||
class Error;
|
||||
|
||||
enum class RenderAPI : u32
|
||||
{
|
||||
None,
|
||||
D3D11,
|
||||
D3D12,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
OpenGLES,
|
||||
Metal
|
||||
};
|
||||
|
||||
class GPUSampler
|
||||
{
|
||||
public:
|
||||
@ -551,7 +541,7 @@ public:
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
|
||||
bool Create(const std::string_view& adapter, const std::string_view& shader_cache_path, u32 shader_cache_version,
|
||||
bool debug_device, bool vsync, bool threaded_presentation,
|
||||
bool debug_device, DisplaySyncMode sync_mode, bool threaded_presentation,
|
||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error);
|
||||
void Destroy();
|
||||
|
||||
@ -646,8 +636,12 @@ public:
|
||||
/// Renders ImGui screen elements. Call before EndPresent().
|
||||
void RenderImGui();
|
||||
|
||||
ALWAYS_INLINE bool IsVsyncEnabled() const { return m_vsync_enabled; }
|
||||
virtual void SetVSync(bool enabled) = 0;
|
||||
ALWAYS_INLINE DisplaySyncMode GetSyncMode() const { return m_sync_mode; }
|
||||
ALWAYS_INLINE bool IsVSyncActive() const
|
||||
{
|
||||
return (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed);
|
||||
}
|
||||
virtual void SetSyncMode(DisplaySyncMode mode);
|
||||
|
||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||
@ -760,8 +754,8 @@ private:
|
||||
protected:
|
||||
static Statistics s_stats;
|
||||
|
||||
DisplaySyncMode m_sync_mode = DisplaySyncMode::Disabled;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_vsync_enabled = false;
|
||||
bool m_debug_device = false;
|
||||
};
|
||||
|
||||
|
||||
24
src/util/gpu_types.h
Normal file
24
src/util/gpu_types.h
Normal file
@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class RenderAPI : u32
|
||||
{
|
||||
None,
|
||||
D3D11,
|
||||
D3D12,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
OpenGLES,
|
||||
Metal
|
||||
};
|
||||
|
||||
enum class DisplaySyncMode : u8
|
||||
{
|
||||
Disabled,
|
||||
VSync,
|
||||
VSyncRelaxed,
|
||||
VRR,
|
||||
Count
|
||||
};
|
||||
@ -263,7 +263,7 @@ public:
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetVSync(bool enabled) override;
|
||||
void SetSyncMode(DisplaySyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
||||
@ -124,12 +124,15 @@ bool MetalDevice::GetHostRefreshRate(float* refresh_rate)
|
||||
return GPUDevice::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void MetalDevice::SetVSync(bool enabled)
|
||||
void MetalDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
m_vsync_enabled = enabled;
|
||||
m_sync_mode = mode;
|
||||
|
||||
if (m_layer != nil)
|
||||
{
|
||||
const bool enabled = (mode == DisplaySyncMode::VSync || mode == DisplaySyncMode::VSyncRelaxed);
|
||||
[m_layer setDisplaySyncEnabled:enabled];
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation,
|
||||
@ -382,7 +385,8 @@ bool MetalDevice::CreateLayer()
|
||||
}
|
||||
});
|
||||
|
||||
[m_layer setDisplaySyncEnabled:m_vsync_enabled];
|
||||
const bool sync_enabled = (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed);
|
||||
[m_layer setDisplaySyncEnabled:sync_enabled];
|
||||
|
||||
DebugAssert(m_layer_pass_desc == nil);
|
||||
m_layer_pass_desc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
|
||||
|
||||
@ -236,12 +236,12 @@ void OpenGLDevice::InsertDebugMessage(const char* msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDevice::SetVSync(bool enabled)
|
||||
void OpenGLDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
if (m_vsync_enabled == enabled)
|
||||
if (m_sync_mode == mode)
|
||||
return;
|
||||
|
||||
m_vsync_enabled = enabled;
|
||||
m_sync_mode = mode;
|
||||
SetSwapInterval();
|
||||
}
|
||||
|
||||
@ -577,7 +577,8 @@ 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_sync_mode == DisplaySyncMode::VSync) ? 1 : ((m_sync_mode == DisplaySyncMode::VSyncRelaxed) ? -1 : 0);
|
||||
GLint current_fbo = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
@ -98,7 +98,7 @@ public:
|
||||
void Draw(u32 vertex_count, u32 base_vertex) override;
|
||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||
|
||||
void SetVSync(bool enabled) override;
|
||||
void SetSyncMode(DisplaySyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
|
||||
<ItemGroup>
|
||||
<ClInclude Include="gpu_types.h" />
|
||||
<ClInclude Include="imgui_animated.h" />
|
||||
<ClInclude Include="audio_stream.h" />
|
||||
<ClInclude Include="cd_image.h" />
|
||||
|
||||
@ -72,6 +72,7 @@
|
||||
<ClInclude Include="opengl_context_egl_wayland.h" />
|
||||
<ClInclude Include="opengl_context_egl_x11.h" />
|
||||
<ClInclude Include="opengl_context_wgl.h" />
|
||||
<ClInclude Include="gpu_types.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="jit_code_buffer.cpp" />
|
||||
|
||||
@ -2022,7 +2022,7 @@ bool VulkanDevice::CreateDevice(const std::string_view& adapter, bool threaded_p
|
||||
|
||||
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, m_sync_mode, m_exclusive_fullscreen_control);
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to create swap chain");
|
||||
@ -2243,7 +2243,7 @@ bool VulkanDevice::UpdateWindow()
|
||||
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, m_sync_mode, m_exclusive_fullscreen_control);
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create swap chain");
|
||||
@ -2319,24 +2319,27 @@ std::string VulkanDevice::GetDriverInfo() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VulkanDevice::SetVSync(bool enabled)
|
||||
void VulkanDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
if (!m_swap_chain || m_vsync_enabled == enabled)
|
||||
if (m_sync_mode == mode)
|
||||
return;
|
||||
|
||||
const DisplaySyncMode prev_mode = m_sync_mode;
|
||||
m_sync_mode = mode;
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
// This swap chain should not be used by the current buffer, thus safe to destroy.
|
||||
WaitForGPUIdle();
|
||||
if (!m_swap_chain->SetVSync(enabled))
|
||||
if (!m_swap_chain->SetSyncMode(mode))
|
||||
{
|
||||
// Try switching back to the old mode..
|
||||
if (!m_swap_chain->SetVSync(m_vsync_enabled))
|
||||
if (!m_swap_chain->SetSyncMode(prev_mode))
|
||||
{
|
||||
Panic("Failed to reset old vsync mode after failure");
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
}
|
||||
|
||||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
bool VulkanDevice::BeginPresent(bool frame_skip)
|
||||
|
||||
@ -126,7 +126,7 @@ public:
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetVSync(bool enabled) override;
|
||||
void SetSyncMode(DisplaySyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
||||
@ -22,9 +22,69 @@
|
||||
|
||||
Log_SetChannel(VulkanDevice);
|
||||
|
||||
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
||||
static VkFormat GetLinearFormat(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
return VK_FORMAT_B8G8R8_UNORM;
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* PresentModeToString(VkPresentModeKHR mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case VK_PRESENT_MODE_IMMEDIATE_KHR:
|
||||
return "VK_PRESENT_MODE_IMMEDIATE_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_MAILBOX_KHR:
|
||||
return "VK_PRESENT_MODE_MAILBOX_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_FIFO_KHR:
|
||||
return "VK_PRESENT_MODE_FIFO_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
|
||||
return "VK_PRESENT_MODE_FIFO_RELAXED_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
|
||||
return "VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
|
||||
return "VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR";
|
||||
|
||||
default:
|
||||
return "UNKNOWN_VK_PRESENT_MODE";
|
||||
}
|
||||
}
|
||||
|
||||
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
static constexpr std::array<VkPresentModeKHR, static_cast<size_t>(DisplaySyncMode::Count)> modes = {{
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR, // Disabled
|
||||
VK_PRESENT_MODE_FIFO_KHR, // VSync
|
||||
VK_PRESENT_MODE_FIFO_RELAXED_KHR, // VSyncRelaxed
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR, // VRR ??
|
||||
}};
|
||||
|
||||
return modes[static_cast<size_t>(mode)];
|
||||
}
|
||||
|
||||
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR requested_present_mode,
|
||||
std::optional<bool> exclusive_fullscreen_control)
|
||||
: m_window_info(wi), m_surface(surface), m_vsync_mode(vsync),
|
||||
: m_window_info(wi), m_surface(surface), m_requested_present_mode(requested_present_mode),
|
||||
m_exclusive_fullscreen_control(exclusive_fullscreen_control)
|
||||
{
|
||||
}
|
||||
@ -160,38 +220,19 @@ void VulkanSwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi,
|
||||
#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,
|
||||
DisplaySyncMode sync_mode,
|
||||
std::optional<bool> exclusive_fullscreen_control)
|
||||
{
|
||||
const VkPresentModeKHR requested_mode = GetPreferredPresentModeForVsyncMode(sync_mode);
|
||||
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, requested_mode, exclusive_fullscreen_control));
|
||||
if (!swap_chain->CreateSwapChain())
|
||||
return nullptr;
|
||||
|
||||
return swap_chain;
|
||||
}
|
||||
|
||||
static VkFormat GetLinearFormat(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
return VK_FORMAT_B8G8R8_UNORM;
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<VkSurfaceFormatKHR> VulkanSwapChain::SelectSurfaceFormat(VkSurfaceKHR surface)
|
||||
{
|
||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
||||
@ -232,44 +273,8 @@ std::optional<VkSurfaceFormatKHR> VulkanSwapChain::SelectSurfaceFormat(VkSurface
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static const char* PresentModeToString(VkPresentModeKHR mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case VK_PRESENT_MODE_IMMEDIATE_KHR:
|
||||
return "VK_PRESENT_MODE_IMMEDIATE_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_MAILBOX_KHR:
|
||||
return "VK_PRESENT_MODE_MAILBOX_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_FIFO_KHR:
|
||||
return "VK_PRESENT_MODE_FIFO_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
|
||||
return "VK_PRESENT_MODE_FIFO_RELAXED_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
|
||||
return "VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
|
||||
return "VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR";
|
||||
|
||||
default:
|
||||
return "UNKNOWN_VK_PRESENT_MODE";
|
||||
}
|
||||
}
|
||||
|
||||
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(bool 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> VulkanSwapChain::SelectPresentMode(VkSurfaceKHR surface, bool vsync)
|
||||
std::optional<VkPresentModeKHR> VulkanSwapChain::SelectPresentMode(VkSurfaceKHR surface,
|
||||
VkPresentModeKHR requested_mode)
|
||||
{
|
||||
VulkanDevice& dev = VulkanDevice::GetInstance();
|
||||
VkResult res;
|
||||
@ -294,18 +299,17 @@ std::optional<VkPresentModeKHR> VulkanSwapChain::SelectPresentMode(VkSurfaceKHR
|
||||
};
|
||||
|
||||
// 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 /*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.
|
||||
selected_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
else if (vsync /*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.
|
||||
@ -317,7 +321,7 @@ std::optional<VkPresentModeKHR> VulkanSwapChain::SelectPresentMode(VkSurfaceKHR
|
||||
selected_mode = present_modes[0];
|
||||
}
|
||||
|
||||
Log_DevPrintf("(SwapChain) Preferred present mode: %s, selected: %s", PresentModeToString(preferred_mode),
|
||||
Log_DevPrintf("(SwapChain) Preferred present mode: %s, selected: %s", PresentModeToString(requested_mode),
|
||||
PresentModeToString(selected_mode));
|
||||
|
||||
return selected_mode;
|
||||
@ -329,7 +333,7 @@ bool VulkanSwapChain::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);
|
||||
std::optional<VkPresentModeKHR> present_mode = SelectPresentMode(m_surface, m_requested_present_mode);
|
||||
if (!surface_format.has_value() || !present_mode.has_value())
|
||||
return false;
|
||||
|
||||
@ -468,6 +472,7 @@ bool VulkanSwapChain::CreateSwapChain()
|
||||
m_window_info.surface_width = std::max(1u, size.width);
|
||||
m_window_info.surface_height = std::max(1u, size.height);
|
||||
m_window_info.surface_format = VulkanDevice::GetFormatForVkFormat(surface_format->format);
|
||||
m_actual_present_mode = present_mode.value();
|
||||
if (m_window_info.surface_format == GPUTexture::Format::Unknown)
|
||||
{
|
||||
Log_ErrorPrintf("Unknown Vulkan surface format %u", static_cast<u32>(surface_format->format));
|
||||
@ -634,12 +639,13 @@ bool VulkanSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_s
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanSwapChain::SetVSync(bool mode)
|
||||
bool VulkanSwapChain::SetSyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
if (m_vsync_mode == mode)
|
||||
const VkPresentModeKHR present_mode = GetPreferredPresentModeForVsyncMode(mode);
|
||||
if (m_requested_present_mode == present_mode)
|
||||
return true;
|
||||
|
||||
m_vsync_mode = mode;
|
||||
m_requested_present_mode = present_mode;
|
||||
|
||||
// Recreate the swap chain with the new present mode.
|
||||
Log_VerbosePrintf("Recreating swap chain to change present mode.");
|
||||
|
||||
@ -25,7 +25,7 @@ public:
|
||||
static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR 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, DisplaySyncMode sync_mode,
|
||||
std::optional<bool> exclusive_fullscreen_control);
|
||||
|
||||
ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||
@ -60,9 +60,12 @@ public:
|
||||
}
|
||||
|
||||
// Returns true if the current present mode is synchronizing (adaptive or hard).
|
||||
ALWAYS_INLINE bool IsPresentModeSynchronizing() const { return (m_vsync_mode /*!= VsyncMode::Off*/); }
|
||||
ALWAYS_INLINE bool IsPresentModeSynchronizing() const
|
||||
{
|
||||
return (m_actual_present_mode == VK_PRESENT_MODE_FIFO_KHR ||
|
||||
m_actual_present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR);
|
||||
}
|
||||
|
||||
VkRenderPass GetRenderPass(VkAttachmentLoadOp load_op) const;
|
||||
VkResult AcquireNextImage();
|
||||
void ReleaseCurrentImage();
|
||||
|
||||
@ -70,19 +73,18 @@ 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(bool mode);
|
||||
bool SetSyncMode(DisplaySyncMode mode);
|
||||
|
||||
private:
|
||||
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
||||
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR requested_present_mode,
|
||||
std::optional<bool> exclusive_fullscreen_control);
|
||||
|
||||
static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface);
|
||||
static std::optional<VkPresentModeKHR> SelectPresentMode(VkSurfaceKHR surface, bool vsync);
|
||||
static std::optional<VkPresentModeKHR> SelectPresentMode(VkSurfaceKHR surface, VkPresentModeKHR requested_mode);
|
||||
|
||||
bool CreateSwapChain();
|
||||
void DestroySwapChain();
|
||||
|
||||
bool SetupSwapChainImages();
|
||||
void DestroySwapChainImages();
|
||||
|
||||
void DestroySurface();
|
||||
@ -109,7 +111,8 @@ private:
|
||||
std::vector<ImageSemaphores> m_semaphores;
|
||||
|
||||
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
||||
bool m_vsync_mode = false;
|
||||
VkPresentModeKHR m_requested_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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user