599 lines
20 KiB
C++
599 lines
20 KiB
C++
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
|
|
|
#if GRAPHICS_API_VULKAN
|
|
|
|
#include "GPUSwapChainVulkan.h"
|
|
#include "RenderToolsVulkan.h"
|
|
#include "Engine/Graphics/GPULimits.h"
|
|
#include "QueueVulkan.h"
|
|
#include "GPUAdapterVulkan.h"
|
|
#include "GPUContextVulkan.h"
|
|
#include "CmdBufferVulkan.h"
|
|
|
|
void BackBufferVulkan::Setup(GPUSwapChainVulkan* window, VkImage backbuffer, PixelFormat format, VkExtent3D extent)
|
|
{
|
|
// Cache handle and set default initial state for the backbuffers
|
|
initResource(VK_IMAGE_LAYOUT_UNDEFINED);
|
|
|
|
Device = window->GetDevice();
|
|
Handle.Init(window->GetDevice(), this, backbuffer, 1, format, MSAALevel::None, extent, VK_IMAGE_VIEW_TYPE_2D);
|
|
RenderingDoneSemaphore = New<SemaphoreVulkan>(Device);
|
|
ImageAcquiredSemaphore = New<SemaphoreVulkan>(Device);
|
|
}
|
|
|
|
void BackBufferVulkan::Release()
|
|
{
|
|
Handle.Release();
|
|
Delete(RenderingDoneSemaphore);
|
|
Delete(ImageAcquiredSemaphore);
|
|
}
|
|
|
|
GPUSwapChainVulkan::GPUSwapChainVulkan(GPUDeviceVulkan* device, Window* window)
|
|
: GPUResourceVulkan(device, StringView::Empty)
|
|
, _surface(VK_NULL_HANDLE)
|
|
, _swapChain(VK_NULL_HANDLE)
|
|
, _currentImageIndex(-1)
|
|
, _semaphoreIndex(0)
|
|
, _acquiredImageIndex(-1)
|
|
, _acquiredSemaphore(nullptr)
|
|
{
|
|
_window = window;
|
|
}
|
|
|
|
void GPUSwapChainVulkan::ReleaseBackBuffer()
|
|
{
|
|
for (int32 i = 0; i < _backBuffers.Count(); i++)
|
|
{
|
|
_backBuffers[i].Release();
|
|
}
|
|
_backBuffers.Clear();
|
|
}
|
|
|
|
void GPUSwapChainVulkan::OnReleaseGPU()
|
|
{
|
|
GPUDeviceLock lock(_device);
|
|
|
|
_device->WaitForGPU();
|
|
|
|
ReleaseBackBuffer();
|
|
|
|
// Release data
|
|
_currentImageIndex = -1;
|
|
_semaphoreIndex = 0;
|
|
_acquiredImageIndex = -1;
|
|
_acquiredSemaphore = nullptr;
|
|
if (_swapChain != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroySwapchainKHR(_device->Device, _swapChain, nullptr);
|
|
_swapChain = VK_NULL_HANDLE;
|
|
}
|
|
if (_surface != VK_NULL_HANDLE)
|
|
{
|
|
vkDestroySurfaceKHR(GPUDeviceVulkan::Instance, _surface, nullptr);
|
|
_surface = VK_NULL_HANDLE;
|
|
}
|
|
_width = _height = 0;
|
|
}
|
|
|
|
bool GPUSwapChainVulkan::IsFullscreen()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void GPUSwapChainVulkan::SetFullscreen(bool isFullscreen)
|
|
{
|
|
#if PLATFORM_ANDROID
|
|
// Not supported
|
|
#else
|
|
if (!_surface)
|
|
return;
|
|
// TODO: support fullscreen mode on Vulkan
|
|
MISSING_CODE("support fullscreen mode on Vulkan");
|
|
#endif
|
|
}
|
|
|
|
GPUTextureView* GPUSwapChainVulkan::GetBackBufferView()
|
|
{
|
|
if (_acquiredImageIndex == -1)
|
|
{
|
|
if (TryPresent(DoAcquireImageIndex) < 0)
|
|
{
|
|
LOG(Fatal, "Swapchain acquire image index failed!");
|
|
}
|
|
ASSERT(_acquiredImageIndex != -1);
|
|
|
|
auto context = _device->MainContext;
|
|
const auto backBuffer = &_backBuffers[_acquiredImageIndex].Handle;
|
|
|
|
auto cmdBufferManager = context->GetCmdBufferManager();
|
|
auto cmdBuffer = cmdBufferManager->GetCmdBuffer();
|
|
|
|
// Transition to render target (typical usage in most cases when calling backbuffer getter)
|
|
context->AddImageBarrier(backBuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
context->FlushBarriers();
|
|
|
|
// Submit here so we can add a dependency with the acquired semaphore
|
|
cmdBuffer->AddWaitSemaphore(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, _acquiredSemaphore);
|
|
cmdBufferManager->SubmitActiveCmdBuffer();
|
|
cmdBufferManager->PrepareForNewActiveCommandBuffer();
|
|
ASSERT(cmdBufferManager->HasPendingActiveCmdBuffer() && cmdBufferManager->GetActiveCmdBuffer()->GetState() == CmdBufferVulkan::State::IsInsideBegin);
|
|
}
|
|
|
|
return &_backBuffers[_acquiredImageIndex].Handle;
|
|
}
|
|
|
|
bool GPUSwapChainVulkan::Resize(int32 width, int32 height)
|
|
{
|
|
// Check if size won't change
|
|
if (width == _width && height == _height)
|
|
return false;
|
|
|
|
// Wait for GPU to flush commands
|
|
_device->WaitForGPU();
|
|
|
|
return CreateSwapChain(width, height);
|
|
}
|
|
|
|
void GPUSwapChainVulkan::CopyBackbuffer(GPUContext* context, GPUTexture* dst)
|
|
{
|
|
const auto contextVulkan = (GPUContextVulkan*)context;
|
|
|
|
const auto dstVulkan = (GPUTextureVulkan*)dst;
|
|
const auto backBuffer = (GPUTextureViewVulkan*)GetBackBufferView();
|
|
|
|
contextVulkan->AddImageBarrier(dstVulkan, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
contextVulkan->AddImageBarrier(backBuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
contextVulkan->FlushBarriers();
|
|
|
|
ASSERT(dstVulkan->MipLevels() == 1 && dstVulkan->ArraySize() == 1 && dstVulkan->Format() == _format);
|
|
VkImageCopy region;
|
|
region.extent = { (uint32_t)dstVulkan->Width(), (uint32_t)dstVulkan->Height(), 1 };
|
|
region.srcOffset = { 0, 0, 0 };
|
|
region.srcSubresource.baseArrayLayer = 0;
|
|
region.srcSubresource.layerCount = 1;
|
|
region.srcSubresource.mipLevel = 0;
|
|
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
region.dstOffset = { 0, 0, 0 };
|
|
region.dstSubresource.baseArrayLayer = 0;
|
|
region.dstSubresource.layerCount = 1;
|
|
region.dstSubresource.mipLevel = 0;
|
|
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
vkCmdCopyImage(contextVulkan->GetCmdBufferManager()->GetCmdBuffer()->GetHandle(), backBuffer->Image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstVulkan->GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
|
}
|
|
|
|
bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
|
{
|
|
// Skip if window handle is missing (eg. Android window is not yet visible)
|
|
auto windowHandle = _window->GetNativePtr();
|
|
if (!windowHandle)
|
|
return false;
|
|
|
|
GPUDeviceLock lock(_device);
|
|
const auto device = _device->Device;
|
|
|
|
// Check if surface has been created before
|
|
if (_surface != VK_NULL_HANDLE)
|
|
{
|
|
// Release previous data
|
|
ReleaseGPU();
|
|
|
|
// Flush removed resources
|
|
_device->DeferredDeletionQueue.ReleaseResources(true);
|
|
}
|
|
|
|
// Create platform-dependent surface
|
|
VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface);
|
|
if (_surface == VK_NULL_HANDLE)
|
|
{
|
|
LOG(Warning, "Failed to create Vulkan surface.");
|
|
return true;
|
|
}
|
|
|
|
const auto& gpu = _device->Adapter->Gpu;
|
|
|
|
// Pick a format for backbuffer
|
|
PixelFormat resultFormat = GPU_BACK_BUFFER_PIXEL_FORMAT;
|
|
VkSurfaceFormatKHR result;
|
|
Platform::MemoryClear(&result, sizeof(result));
|
|
{
|
|
uint32 surfaceFormatsCount;
|
|
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, nullptr));
|
|
ASSERT(surfaceFormatsCount > 0);
|
|
|
|
Array<VkSurfaceFormatKHR> surfaceFormats;
|
|
surfaceFormats.AddZeroed(surfaceFormatsCount);
|
|
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, surfaceFormats.Get()));
|
|
|
|
if (resultFormat != PixelFormat::Unknown)
|
|
{
|
|
bool found = false;
|
|
if (FORMAT_FEATURES_ARE_SUPPORTED(_device->FeaturesPerFormat[(int32)resultFormat].Support, FormatSupport::RenderTarget))
|
|
{
|
|
const VkFormat requested = RenderToolsVulkan::ToVulkanFormat(resultFormat);
|
|
for (int32 i = 0; i < surfaceFormats.Count(); i++)
|
|
{
|
|
if (surfaceFormats[i].format == requested)
|
|
{
|
|
result = surfaceFormats[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
LOG(Warning, "Requested pixel format {0} not supported by this swapchain. Falling back to supported swapchain formats...", (uint32)resultFormat);
|
|
resultFormat = PixelFormat::Unknown;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(Warning, "Requested pixel format {0} is not supported by this Vulkan implementation", (uint32)resultFormat);
|
|
resultFormat = PixelFormat::Unknown;
|
|
}
|
|
}
|
|
|
|
if (resultFormat == PixelFormat::Unknown)
|
|
{
|
|
for (int32 i = 0; i < surfaceFormats.Count(); i++)
|
|
{
|
|
// Reverse lookup
|
|
ASSERT(surfaceFormats[i].format != VK_FORMAT_UNDEFINED);
|
|
for (int32 pixelFormat = 0; pixelFormat < static_cast<int32>(PixelFormat::MAX); pixelFormat++)
|
|
{
|
|
if (surfaceFormats[i].format == RenderToolsVulkan::ToVulkanFormat(static_cast<PixelFormat>(pixelFormat)))
|
|
{
|
|
resultFormat = static_cast<PixelFormat>(pixelFormat);
|
|
result = surfaceFormats[i];
|
|
LOG(Info, "No swapchain format requested, picking up Vulkan format {0}", (uint32)result.format);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (resultFormat != PixelFormat::Unknown)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resultFormat == PixelFormat::Unknown)
|
|
{
|
|
LOG(Warning, "Can't find a proper pixel format for the swapchain, trying to pick up the first available");
|
|
|
|
const VkFormat format = RenderToolsVulkan::ToVulkanFormat(resultFormat);
|
|
bool supported = false;
|
|
for (int32 i = 0; i < surfaceFormats.Count(); i++)
|
|
{
|
|
if (surfaceFormats[i].format == format)
|
|
{
|
|
supported = true;
|
|
result = surfaceFormats[i];
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(supported);
|
|
|
|
String msg;
|
|
for (int32 index = 0; index < surfaceFormats.Count(); index++)
|
|
{
|
|
if (index == 0)
|
|
{
|
|
msg += TEXT("(");
|
|
}
|
|
else
|
|
{
|
|
msg += TEXT(", ");
|
|
}
|
|
msg += StringUtils::ToString((int32)surfaceFormats[index].format);
|
|
}
|
|
if (surfaceFormats.HasItems())
|
|
{
|
|
msg += TEXT(")");
|
|
}
|
|
LOG(Error, "Unable to find a pixel format for the swapchain; swapchain returned {0} Vulkan formats {1}", surfaceFormats.Count(), *msg);
|
|
}
|
|
}
|
|
result.format = RenderToolsVulkan::ToVulkanFormat(resultFormat);
|
|
_format = resultFormat;
|
|
|
|
// Prepare present queue
|
|
_device->SetupPresentQueue(_surface);
|
|
|
|
// Calculate the swap chain present mode
|
|
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
{
|
|
uint32 presentModesCount = 0;
|
|
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, _surface, &presentModesCount, nullptr));
|
|
ASSERT(presentModesCount > 0);
|
|
|
|
Array<VkPresentModeKHR> presentModes;
|
|
presentModes.Resize(presentModesCount);
|
|
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, _surface, &presentModesCount, presentModes.Get()));
|
|
|
|
bool foundPresentModeMailbox = false;
|
|
bool foundPresentModeImmediate = false;
|
|
bool foundPresentModeFifo = false;
|
|
|
|
for (size_t i = 0; i < presentModesCount; i++)
|
|
{
|
|
switch (presentModes[(int32)i])
|
|
{
|
|
case VK_PRESENT_MODE_MAILBOX_KHR:
|
|
foundPresentModeMailbox = true;
|
|
break;
|
|
case VK_PRESENT_MODE_IMMEDIATE_KHR:
|
|
foundPresentModeImmediate = true;
|
|
break;
|
|
case VK_PRESENT_MODE_FIFO_KHR:
|
|
foundPresentModeFifo = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundPresentModeMailbox)
|
|
{
|
|
presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
}
|
|
else if (foundPresentModeImmediate)
|
|
{
|
|
presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
|
}
|
|
else if (foundPresentModeFifo)
|
|
{
|
|
presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
}
|
|
else
|
|
{
|
|
LOG(Warning, "Couldn't find desired Vulkan present mode! Using {0}", static_cast<int32>(presentModes[0]));
|
|
presentMode = presentModes[0];
|
|
}
|
|
}
|
|
|
|
// Check the surface properties and formats
|
|
VkSurfaceCapabilitiesKHR surfProperties;
|
|
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, _surface, &surfProperties));
|
|
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
|
if (surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
|
|
compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
width = Math::Clamp<int32>(width, surfProperties.minImageExtent.width, surfProperties.maxImageExtent.width);
|
|
height = Math::Clamp<int32>(height, surfProperties.minImageExtent.height, surfProperties.maxImageExtent.height);
|
|
VkSwapchainCreateInfoKHR swapChainInfo;
|
|
RenderToolsVulkan::ZeroStruct(swapChainInfo, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
|
|
swapChainInfo.surface = _surface;
|
|
swapChainInfo.minImageCount = VULKAN_BACK_BUFFERS_COUNT;
|
|
swapChainInfo.imageFormat = result.format;
|
|
swapChainInfo.imageColorSpace = result.colorSpace;
|
|
swapChainInfo.imageExtent.width = width;
|
|
swapChainInfo.imageExtent.height = height;
|
|
swapChainInfo.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
#if GPU_USE_WINDOW_SRV
|
|
swapChainInfo.imageUsage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
#endif
|
|
swapChainInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
|
swapChainInfo.imageArrayLayers = 1;
|
|
swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
swapChainInfo.presentMode = presentMode;
|
|
swapChainInfo.oldSwapchain = VK_NULL_HANDLE;
|
|
swapChainInfo.clipped = VK_TRUE;
|
|
swapChainInfo.compositeAlpha = compositeAlpha;
|
|
|
|
// Create swap chain
|
|
VkBool32 supportsPresent;
|
|
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(gpu, _device->PresentQueue->GetFamilyIndex(), _surface, &supportsPresent));
|
|
ASSERT(supportsPresent);
|
|
VALIDATE_VULKAN_RESULT(vkCreateSwapchainKHR(device, &swapChainInfo, nullptr, &_swapChain));
|
|
|
|
// Cache data
|
|
_width = width;
|
|
_height = height;
|
|
|
|
// Setup back buffers
|
|
{
|
|
uint32 imagesCount;
|
|
VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, nullptr));
|
|
ASSERT(imagesCount >= VULKAN_BACK_BUFFERS_COUNT && imagesCount <= VULKAN_BACK_BUFFERS_COUNT_MAX);
|
|
|
|
Array<VkImage, FixedAllocation<VULKAN_BACK_BUFFERS_COUNT_MAX>> images;
|
|
images.Resize(imagesCount);
|
|
VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, images.Get()));
|
|
|
|
_backBuffers.Resize(imagesCount);
|
|
VkExtent3D extent;
|
|
extent.width = _width;
|
|
extent.height = _height;
|
|
extent.depth = 1;
|
|
for (uint32 i = 0; i < imagesCount; i++)
|
|
{
|
|
_backBuffers[i].Setup(this, images[i], _format, extent);
|
|
}
|
|
}
|
|
|
|
// Calculate memory usage
|
|
_memoryUsage = CalculateTextureMemoryUsage(_format, _width, _height, 1) * _backBuffers.Count();
|
|
|
|
return false;
|
|
}
|
|
|
|
GPUSwapChainVulkan::Status GPUSwapChainVulkan::Present(QueueVulkan* presentQueue, SemaphoreVulkan* backBufferRenderingDoneSemaphore)
|
|
{
|
|
if (_currentImageIndex == -1)
|
|
{
|
|
return Status::Ok;
|
|
}
|
|
|
|
VkPresentInfoKHR presentInfo;
|
|
RenderToolsVulkan::ZeroStruct(presentInfo, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR);
|
|
VkSemaphore semaphore;
|
|
if (backBufferRenderingDoneSemaphore)
|
|
{
|
|
presentInfo.waitSemaphoreCount = 1;
|
|
semaphore = backBufferRenderingDoneSemaphore->GetHandle();
|
|
presentInfo.pWaitSemaphores = &semaphore;
|
|
}
|
|
presentInfo.swapchainCount = 1;
|
|
presentInfo.pSwapchains = &_swapChain;
|
|
presentInfo.pImageIndices = (uint32*)&_currentImageIndex;
|
|
|
|
const VkResult presentResult = vkQueuePresentKHR(presentQueue->GetHandle(), &presentInfo);
|
|
|
|
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR)
|
|
{
|
|
return Status::Outdated;
|
|
}
|
|
if (presentResult == VK_ERROR_SURFACE_LOST_KHR)
|
|
{
|
|
return Status::LostSurface;
|
|
}
|
|
if (presentResult != VK_SUCCESS && presentResult != VK_SUBOPTIMAL_KHR)
|
|
{
|
|
VALIDATE_VULKAN_RESULT(presentResult);
|
|
}
|
|
|
|
return Status::Ok;
|
|
}
|
|
|
|
int32 GPUSwapChainVulkan::DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData)
|
|
{
|
|
return viewport->_acquiredImageIndex = viewport->AcquireNextImage(&viewport->_acquiredSemaphore);
|
|
}
|
|
|
|
int32 GPUSwapChainVulkan::DoPresent(GPUSwapChainVulkan* viewport, void* customData)
|
|
{
|
|
return (int32)viewport->Present((QueueVulkan*)customData, viewport->_backBuffers[viewport->_acquiredImageIndex].RenderingDoneSemaphore);
|
|
}
|
|
|
|
int32 GPUSwapChainVulkan::TryPresent(Function<int32(GPUSwapChainVulkan*, void*)> job, void* customData, bool skipOnOutOfDate)
|
|
{
|
|
int32 attemptsPending = 4;
|
|
int32 status = job(this, customData);
|
|
|
|
while (status < 0 && attemptsPending > 0)
|
|
{
|
|
if (status == (int32)Status::Outdated)
|
|
{
|
|
//LOG(Warning, "Swapchain is out of date");
|
|
if (skipOnOutOfDate)
|
|
return status;
|
|
}
|
|
else if (status == (int32)Status::LostSurface)
|
|
{
|
|
LOG(Warning, "Swapchain surface lost");
|
|
}
|
|
else
|
|
{
|
|
CRASH;
|
|
}
|
|
|
|
// Recreate swapchain
|
|
ASSERT(_swapChain != VK_NULL_HANDLE);
|
|
int32 width = _width, height = _height;
|
|
ReleaseGPU();
|
|
CreateSwapChain(width, height);
|
|
|
|
// Flush commands
|
|
_device->GetMainContext()->Flush();
|
|
_device->WaitForGPU();
|
|
|
|
status = job(this, customData);
|
|
|
|
attemptsPending--;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int32 GPUSwapChainVulkan::AcquireNextImage(SemaphoreVulkan** outSemaphore)
|
|
{
|
|
ASSERT(_swapChain && _backBuffers.HasItems());
|
|
|
|
uint32 imageIndex = 0;
|
|
const int32 prevSemaphoreIndex = _semaphoreIndex;
|
|
_semaphoreIndex = (_semaphoreIndex + 1) % _backBuffers.Count();
|
|
const auto semaphore = _backBuffers[_semaphoreIndex].ImageAcquiredSemaphore;
|
|
|
|
const VkResult result = vkAcquireNextImageKHR(
|
|
_device->Device,
|
|
_swapChain,
|
|
UINT64_MAX,
|
|
semaphore->GetHandle(),
|
|
VK_NULL_HANDLE,
|
|
&imageIndex);
|
|
|
|
if (result == VK_ERROR_OUT_OF_DATE_KHR)
|
|
{
|
|
_semaphoreIndex = prevSemaphoreIndex;
|
|
return (int32)Status::Outdated;
|
|
}
|
|
|
|
if (result == VK_ERROR_SURFACE_LOST_KHR)
|
|
{
|
|
_semaphoreIndex = prevSemaphoreIndex;
|
|
return (int32)Status::LostSurface;
|
|
}
|
|
|
|
*outSemaphore = semaphore;
|
|
|
|
if (result == VK_ERROR_VALIDATION_FAILED_EXT)
|
|
{
|
|
LOG(Fatal, "vkAcquireNextImageKHR failed with validation error");
|
|
}
|
|
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
|
|
{
|
|
#if GPU_ENABLE_ASSERTION
|
|
RenderToolsVulkan::LogVkResult(result, __FILE__, __LINE__);
|
|
#endif
|
|
}
|
|
_currentImageIndex = (int32)imageIndex;
|
|
|
|
return _currentImageIndex;
|
|
}
|
|
|
|
void GPUSwapChainVulkan::Present(bool vsync)
|
|
{
|
|
// Skip if there was no rendering to the backbuffer
|
|
if (_acquiredImageIndex == -1)
|
|
return;
|
|
|
|
// Ensure that backbuffer has been acquired before presenting it to the window
|
|
const auto backBuffer = (GPUTextureViewVulkan*)GetBackBufferView();
|
|
|
|
// Ensure to keep backbuffer in a proper layout
|
|
auto context = _device->MainContext;
|
|
context->AddImageBarrier(backBuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
|
context->FlushBarriers();
|
|
|
|
context->GetCmdBufferManager()->SubmitActiveCmdBuffer(_backBuffers[_acquiredImageIndex].RenderingDoneSemaphore);
|
|
|
|
// Present the back buffer to the viewport window
|
|
const auto result = TryPresent(DoPresent, _device->PresentQueue, true);
|
|
if (result == (int32)Status::Outdated)
|
|
{
|
|
// Failed to present, window can be minimized or doesn't want to swap the buffers so just ignore the present
|
|
if (_window->IsMinimized())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Rebuild swapchain for the next present
|
|
int32 width = _width, height = _height;
|
|
ReleaseGPU();
|
|
CreateSwapChain(width, height);
|
|
_device->GetMainContext()->Flush();
|
|
_device->WaitForGPU();
|
|
return;
|
|
}
|
|
if (result < 0)
|
|
{
|
|
LOG(Fatal, "Swapchain present failed!");
|
|
}
|
|
|
|
// Release the back buffer
|
|
_acquiredImageIndex = -1;
|
|
|
|
// Base
|
|
GPUSwapChain::Present(vsync);
|
|
}
|
|
|
|
#endif
|