Add VSync support to Vulkan
This commit is contained in:
@@ -622,6 +622,16 @@ void GPUDevice::DumpResources()
|
||||
|
||||
extern void ClearVertexLayoutCache();
|
||||
|
||||
bool UseVSync()
|
||||
{
|
||||
bool vsync = Graphics::UseVSync;
|
||||
if (CommandLine::Options.NoVSync.HasValue())
|
||||
vsync = !CommandLine::Options.NoVSync.GetValue();
|
||||
else if (CommandLine::Options.VSync.HasValue())
|
||||
vsync = CommandLine::Options.VSync.GetValue();
|
||||
return vsync;
|
||||
}
|
||||
|
||||
void GPUDevice::preDispose()
|
||||
{
|
||||
Locker.Lock();
|
||||
@@ -665,26 +675,26 @@ void GPUDevice::DrawEnd()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Present");
|
||||
|
||||
// Check if use VSync
|
||||
bool useVSync = Graphics::UseVSync;
|
||||
if (CommandLine::Options.NoVSync.HasValue())
|
||||
useVSync = !CommandLine::Options.NoVSync.GetValue();
|
||||
else if (CommandLine::Options.VSync.HasValue())
|
||||
useVSync = CommandLine::Options.VSync.GetValue();
|
||||
|
||||
// Find index of the last rendered window task (use vsync only on the last window)
|
||||
int32 lastWindowIndex = -1;
|
||||
// Check if use VSync (prioritize the last window, in case of multi-window in Editor)
|
||||
bool useVSync = UseVSync();
|
||||
int32 vsyncTask = -1;
|
||||
for (int32 i = RenderTask::Tasks.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
const auto task = RenderTask::Tasks[i];
|
||||
if (task && task->LastUsedFrame == Engine::FrameCount && task->SwapChain && task->SwapChain->IsReady())
|
||||
{
|
||||
lastWindowIndex = i;
|
||||
vsyncTask = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_rendererType == RendererType::Vulkan && RenderTask::Tasks.Contains(_lastVSyncTask))
|
||||
{
|
||||
// On Vulkan, maintain the last window that was VSynced to avoid recreating swapchain too often (eg. when using context menus or tooltips)
|
||||
vsyncTask = RenderTask::Tasks.Find(_lastVSyncTask);
|
||||
}
|
||||
|
||||
// Call present on all used tasks
|
||||
_lastVSyncTask = nullptr;
|
||||
int32 presentCount = 0;
|
||||
bool anyVSync = false;
|
||||
#if COMPILE_WITH_PROFILER
|
||||
@@ -696,7 +706,7 @@ void GPUDevice::DrawEnd()
|
||||
if (task && task->LastUsedFrame == Engine::FrameCount && task->SwapChain && task->SwapChain->IsReady())
|
||||
{
|
||||
bool vsync = useVSync;
|
||||
if (lastWindowIndex != i)
|
||||
if (vsyncTask != i)
|
||||
{
|
||||
// Perform VSync only on the last window
|
||||
vsync = false;
|
||||
@@ -711,6 +721,8 @@ void GPUDevice::DrawEnd()
|
||||
|
||||
anyVSync |= vsync;
|
||||
task->OnPresent(vsync);
|
||||
if (vsync)
|
||||
_lastVSyncTask = task;
|
||||
presentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,7 @@ protected:
|
||||
PrivateData* _res;
|
||||
Array<GPUResource*> _resources;
|
||||
CriticalSection _resourcesLock;
|
||||
RenderTask* _lastVSyncTask = nullptr;
|
||||
|
||||
void OnRequestingExit();
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "GPUContextVulkan.h"
|
||||
#include "CmdBufferVulkan.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/Graphics.h"
|
||||
#include "Engine/Scripting/Enums.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
@@ -153,6 +155,14 @@ void GPUSwapChainVulkan::Begin(RenderTask* task)
|
||||
backBuffer.SubmitCmdBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild swapchain if need to
|
||||
if (_vsyncPending != _vsyncCurrent)
|
||||
{
|
||||
LOG(Info, "Changing VSync mode to {}", _vsyncPending ? TEXT("on") : TEXT("off"));
|
||||
_device->WaitForGPU();
|
||||
CreateSwapChain(_width, _height);
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUSwapChainVulkan::Resize(int32 width, int32 height)
|
||||
@@ -213,6 +223,14 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
// Flush removed resources
|
||||
_device->DeferredDeletionQueue.ReleaseResources(true);
|
||||
}
|
||||
if (_vsyncInit)
|
||||
{
|
||||
// Guess the VSync value for the 1st time (Present has not been called yet)
|
||||
extern bool UseVSync();
|
||||
_vsyncPending = UseVSync();
|
||||
_vsyncPending &= _window == Engine::MainWindow; // Don't use VSync on new context menus or tooltips in Editor
|
||||
_vsyncInit = false;
|
||||
}
|
||||
ASSERT(_surface == VK_NULL_HANDLE);
|
||||
ASSERT_LOW_LAYER(_backBuffers.Count() == 0);
|
||||
|
||||
@@ -329,14 +347,14 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
Array<VkPresentModeKHR, InlinedAllocation<8>> presentModes;
|
||||
presentModes.Resize(presentModesCount);
|
||||
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, _surface, &presentModesCount, presentModes.Get()));
|
||||
if (presentModes.Contains(VK_PRESENT_MODE_MAILBOX_KHR))
|
||||
{
|
||||
presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
else if (presentModes.Contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
|
||||
if (!_vsyncPending && presentModes.Contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
|
||||
{
|
||||
presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
else if (!_vsyncPending && presentModes.Contains(VK_PRESENT_MODE_MAILBOX_KHR))
|
||||
{
|
||||
presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
else if (presentModes.Contains(VK_PRESENT_MODE_FIFO_KHR))
|
||||
{
|
||||
presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
@@ -406,6 +424,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
// Cache data
|
||||
_width = width;
|
||||
_height = height;
|
||||
_vsyncCurrent = _vsyncPending;
|
||||
|
||||
// Setup back buffers
|
||||
{
|
||||
@@ -567,6 +586,9 @@ void GPUSwapChainVulkan::Present(bool vsync)
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
|
||||
// Update pending VSync value based on the present mode
|
||||
_vsyncPending = vsync;
|
||||
|
||||
// Ensure that backbuffer has been acquired before presenting it to the window
|
||||
const auto backBuffer = (GPUTextureViewVulkan*)GetBackBufferView();
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ private:
|
||||
int32 _currentImageIndex;
|
||||
int32 _semaphoreIndex;
|
||||
int32 _acquiredImageIndex;
|
||||
bool _vsyncCurrent = false, _vsyncPending = false, _vsyncInit = true;
|
||||
Array<BackBufferVulkan, FixedAllocation<VULKAN_BACK_BUFFERS_COUNT_MAX>> _backBuffers;
|
||||
SemaphoreVulkan* _acquiredSemaphore;
|
||||
|
||||
@@ -75,7 +76,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the Vulkan surface.
|
||||
/// </summary>
|
||||
/// <returns>The surface object.</returns>
|
||||
FORCE_INLINE VkSurfaceKHR GetSurface() const
|
||||
{
|
||||
return _surface;
|
||||
@@ -84,7 +84,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the Vulkan surface swap chain.
|
||||
/// </summary>
|
||||
/// <returns>The swap chain object.</returns>
|
||||
FORCE_INLINE VkSwapchainKHR GetSwapChain() const
|
||||
{
|
||||
return _swapChain;
|
||||
|
||||
Reference in New Issue
Block a user