VulkanRenderer: Handle window resize
This commit is contained in:
@@ -7,22 +7,24 @@
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderer.hpp>
|
||||
#include <Nazara/Renderer/CommandPool.hpp>
|
||||
#include <Nazara/Renderer/RenderWindow.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
OpenGLRenderWindow::OpenGLRenderWindow() :
|
||||
OpenGLRenderWindow::OpenGLRenderWindow(RenderWindow& owner) :
|
||||
m_currentFrame(0),
|
||||
m_framebuffer(*this)
|
||||
m_framebuffer(*this),
|
||||
m_owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
OpenGLRenderImage& OpenGLRenderWindow::Acquire()
|
||||
RenderFrame OpenGLRenderWindow::Acquire()
|
||||
{
|
||||
return m_renderImage[m_currentFrame];
|
||||
return RenderFrame(&m_renderImage[m_currentFrame], false);
|
||||
}
|
||||
|
||||
bool OpenGLRenderWindow::Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters)
|
||||
bool OpenGLRenderWindow::Create(RendererImpl* renderer, RenderSurface* surface, const RenderWindowParameters& parameters)
|
||||
{
|
||||
DummySurface* dummySurface = static_cast<DummySurface*>(surface);
|
||||
OpenGLRenderer* glRenderer = static_cast<OpenGLRenderer*>(renderer);
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace Nz
|
||||
return std::make_unique<DummySurface>();
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderWindowImpl> OpenGLRenderer::CreateRenderWindowImpl()
|
||||
std::unique_ptr<RenderWindowImpl> OpenGLRenderer::CreateRenderWindowImpl(RenderWindow& owner)
|
||||
{
|
||||
return std::make_unique<OpenGLRenderWindow>();
|
||||
return std::make_unique<OpenGLRenderWindow>(owner);
|
||||
}
|
||||
|
||||
std::shared_ptr<RenderDevice> OpenGLRenderer::InstanciateRenderDevice(std::size_t deviceIndex)
|
||||
|
||||
44
src/Nazara/Renderer/RenderFrame.cpp
Normal file
44
src/Nazara/Renderer/RenderFrame.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2020 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/RenderFrame.hpp>
|
||||
#include <Nazara/Renderer/RenderImage.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
void RenderFrame::Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags)
|
||||
{
|
||||
if (!m_image)
|
||||
throw std::runtime_error("frame is either invalid or has already been presented");
|
||||
|
||||
return m_image->Execute(callback, queueTypeFlags);
|
||||
}
|
||||
|
||||
UploadPool& RenderFrame::GetUploadPool()
|
||||
{
|
||||
if (!m_image)
|
||||
throw std::runtime_error("frame is either invalid or has already been presented");
|
||||
|
||||
return m_image->GetUploadPool();
|
||||
}
|
||||
|
||||
void RenderFrame::SubmitCommandBuffer(CommandBuffer* commandBuffer, QueueTypeFlags queueTypeFlags)
|
||||
{
|
||||
if (!m_image)
|
||||
throw std::runtime_error("frame is either invalid or has already been presented");
|
||||
|
||||
m_image->SubmitCommandBuffer(commandBuffer, queueTypeFlags);
|
||||
}
|
||||
|
||||
void RenderFrame::Present()
|
||||
{
|
||||
if (!m_image)
|
||||
throw std::runtime_error("frame is either invalid or has already been presented");
|
||||
|
||||
m_image->Present();
|
||||
m_image = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,8 @@ namespace Nz
|
||||
return false;
|
||||
}
|
||||
|
||||
auto impl = rendererImpl->CreateRenderWindowImpl();
|
||||
if (!impl->Create(rendererImpl, surface.get(), GetSize(), m_parameters))
|
||||
auto impl = rendererImpl->CreateRenderWindowImpl(*this);
|
||||
if (!impl->Create(rendererImpl, surface.get(), m_parameters))
|
||||
{
|
||||
NazaraError("Failed to create render window implementation: " + Error::GetLastError());
|
||||
return false;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <Nazara/Math/Vector2.hpp>
|
||||
#include <Nazara/Renderer/RenderWindow.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
#include <Nazara/VulkanRenderer/Vulkan.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
|
||||
@@ -18,9 +19,11 @@
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
VkRenderWindow::VkRenderWindow() :
|
||||
VkRenderWindow::VkRenderWindow(RenderWindow& owner) :
|
||||
m_currentFrame(0),
|
||||
m_depthStencilFormat(VK_FORMAT_MAX_ENUM)
|
||||
m_depthStencilFormat(VK_FORMAT_MAX_ENUM),
|
||||
m_owner(owner),
|
||||
m_shouldRecreateSwapchain(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -35,8 +38,26 @@ namespace Nz
|
||||
m_swapchain.Destroy();
|
||||
}
|
||||
|
||||
VulkanRenderImage& VkRenderWindow::Acquire()
|
||||
RenderFrame VkRenderWindow::Acquire()
|
||||
{
|
||||
bool invalidateFramebuffer = false;
|
||||
|
||||
Vector2ui size = m_owner.GetSize();
|
||||
// Special case: window is minimized
|
||||
if (size == Nz::Vector2ui::Zero() || m_owner.IsMinimized())
|
||||
return RenderFrame();
|
||||
|
||||
if (m_shouldRecreateSwapchain || size != m_swapchainSize)
|
||||
{
|
||||
Vk::Surface& vulkanSurface = static_cast<VulkanSurface*>(m_owner.GetSurface())->GetSurface();
|
||||
|
||||
if (!CreateSwapchain(vulkanSurface, size))
|
||||
throw std::runtime_error("failed to recreate swapchain");
|
||||
|
||||
m_shouldRecreateSwapchain = false;
|
||||
invalidateFramebuffer = true;
|
||||
}
|
||||
|
||||
VulkanRenderImage& currentFrame = m_concurrentImageData[m_currentFrame];
|
||||
Vk::Fence& inFlightFence = currentFrame.GetInFlightFence();
|
||||
|
||||
@@ -44,8 +65,33 @@ namespace Nz
|
||||
inFlightFence.Wait();
|
||||
|
||||
UInt32 imageIndex;
|
||||
if (!m_swapchain.AcquireNextImage(std::numeric_limits<UInt64>::max(), currentFrame.GetImageAvailableSemaphore(), VK_NULL_HANDLE, &imageIndex))
|
||||
throw std::runtime_error("Failed to acquire next image: " + TranslateVulkanError(m_swapchain.GetLastErrorCode()));
|
||||
m_swapchain.AcquireNextImage(std::numeric_limits<UInt64>::max(), currentFrame.GetImageAvailableSemaphore(), VK_NULL_HANDLE, &imageIndex);
|
||||
|
||||
switch (m_swapchain.GetLastErrorCode())
|
||||
{
|
||||
case VK_SUCCESS:
|
||||
break;
|
||||
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
m_shouldRecreateSwapchain = true; //< Recreate swapchain next time
|
||||
break;
|
||||
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
m_shouldRecreateSwapchain = true;
|
||||
return Acquire();
|
||||
|
||||
// Not expected (since timeout is infinite)
|
||||
case VK_TIMEOUT:
|
||||
case VK_NOT_READY:
|
||||
// Unhandled errors
|
||||
case VK_ERROR_DEVICE_LOST:
|
||||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
case VK_ERROR_SURFACE_LOST_KHR: //< TODO: Handle it by recreating the surface?
|
||||
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
|
||||
default:
|
||||
throw std::runtime_error("Failed to acquire next image: " + TranslateVulkanError(m_swapchain.GetLastErrorCode()));
|
||||
}
|
||||
|
||||
if (m_inflightFences[imageIndex])
|
||||
m_inflightFences[imageIndex]->Wait();
|
||||
@@ -55,10 +101,10 @@ namespace Nz
|
||||
|
||||
currentFrame.Reset(imageIndex);
|
||||
|
||||
return currentFrame;
|
||||
return RenderFrame(¤tFrame, invalidateFramebuffer);
|
||||
}
|
||||
|
||||
bool VkRenderWindow::Create(RendererImpl* /*renderer*/, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters)
|
||||
bool VkRenderWindow::Create(RendererImpl* /*renderer*/, RenderSurface* surface, const RenderWindowParameters& parameters)
|
||||
{
|
||||
const auto& deviceInfo = Vulkan::GetPhysicalDevices()[0];
|
||||
|
||||
@@ -156,61 +202,18 @@ namespace Nz
|
||||
}
|
||||
}
|
||||
|
||||
if (!SetupSwapchain(deviceInfo, vulkanSurface, size))
|
||||
{
|
||||
NazaraError("Failed to create swapchain");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM && !SetupDepthBuffer(size))
|
||||
{
|
||||
NazaraError("Failed to create depth buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetupRenderPass())
|
||||
{
|
||||
NazaraError("Failed to create render pass");
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32 imageCount = m_swapchain.GetBufferCount();
|
||||
|
||||
// Framebuffers
|
||||
m_inflightFences.resize(imageCount);
|
||||
|
||||
Nz::StackArray<Vk::Framebuffer> framebuffers = NazaraStackArray(Vk::Framebuffer, imageCount);
|
||||
for (UInt32 i = 0; i < imageCount; ++i)
|
||||
if (!CreateSwapchain(vulkanSurface, m_owner.GetSize()))
|
||||
{
|
||||
std::array<VkImageView, 2> attachments = { m_swapchain.GetBuffer(i).view, m_depthBufferView };
|
||||
|
||||
VkFramebufferCreateInfo frameBufferCreate = {
|
||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
m_renderPass->GetRenderPass(),
|
||||
(attachments[1] != VK_NULL_HANDLE) ? 2U : 1U,
|
||||
attachments.data(),
|
||||
size.x,
|
||||
size.y,
|
||||
1U
|
||||
};
|
||||
|
||||
if (!framebuffers[i].Create(*m_device, frameBufferCreate))
|
||||
{
|
||||
NazaraError("Failed to create framebuffer for image #" + String::Number(i) + ": " + TranslateVulkanError(framebuffers[i].GetLastErrorCode()));
|
||||
return false;
|
||||
}
|
||||
NazaraError("failed to create swapchain");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_framebuffer.emplace(framebuffers.data(), framebuffers.size());
|
||||
|
||||
const std::size_t MaxConcurrentImage = imageCount;
|
||||
m_concurrentImageData.reserve(MaxConcurrentImage);
|
||||
|
||||
for (std::size_t i = 0; i < MaxConcurrentImage; ++i)
|
||||
m_concurrentImageData.emplace_back(*this);
|
||||
|
||||
m_clock.Restart();
|
||||
|
||||
return true;
|
||||
@@ -237,9 +240,60 @@ namespace Nz
|
||||
return std::make_unique<VulkanCommandPool>(*m_device, queueFamilyIndex);
|
||||
}
|
||||
|
||||
const VulkanRenderPass& VkRenderWindow::GetRenderPass() const
|
||||
void VkRenderWindow::Present(UInt32 imageIndex, VkSemaphore waitSemaphore)
|
||||
{
|
||||
return *m_renderPass;
|
||||
NazaraAssert(imageIndex < m_inflightFences.size(), "Invalid image index");
|
||||
|
||||
m_currentFrame = (m_currentFrame + 1) % m_inflightFences.size();
|
||||
|
||||
m_presentQueue.Present(m_swapchain, imageIndex, waitSemaphore);
|
||||
|
||||
switch (m_presentQueue.GetLastErrorCode())
|
||||
{
|
||||
case VK_SUCCESS:
|
||||
break;
|
||||
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
{
|
||||
// Recreate swapchain next time
|
||||
m_shouldRecreateSwapchain = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Unhandled errors
|
||||
case VK_ERROR_DEVICE_LOST:
|
||||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
case VK_ERROR_SURFACE_LOST_KHR: //< TODO: Handle it by recreating the surface?
|
||||
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
|
||||
default:
|
||||
throw std::runtime_error("Failed to present image: " + TranslateVulkanError(m_swapchain.GetLastErrorCode()));
|
||||
}
|
||||
}
|
||||
|
||||
bool VkRenderWindow::CreateSwapchain(Vk::Surface& surface, const Vector2ui& size)
|
||||
{
|
||||
assert(m_device);
|
||||
if (!SetupSwapchain(m_device->GetPhysicalDeviceInfo(), surface, size))
|
||||
{
|
||||
NazaraError("Failed to create swapchain");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM && !SetupDepthBuffer(size))
|
||||
{
|
||||
NazaraError("Failed to create depth buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetupFrameBuffers(size))
|
||||
{
|
||||
NazaraError("failed to create framebuffers");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VkRenderWindow::SetupDepthBuffer(const Vector2ui& size)
|
||||
@@ -312,6 +366,38 @@ namespace Nz
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VkRenderWindow::SetupFrameBuffers(const Vector2ui& size)
|
||||
{
|
||||
UInt32 imageCount = m_swapchain.GetBufferCount();
|
||||
|
||||
Nz::StackArray<Vk::Framebuffer> framebuffers = NazaraStackArray(Vk::Framebuffer, imageCount);
|
||||
for (UInt32 i = 0; i < imageCount; ++i)
|
||||
{
|
||||
std::array<VkImageView, 2> attachments = { m_swapchain.GetBuffer(i).view, m_depthBufferView };
|
||||
|
||||
VkFramebufferCreateInfo frameBufferCreate = {
|
||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
m_renderPass->GetRenderPass(),
|
||||
(attachments[1] != VK_NULL_HANDLE) ? 2U : 1U,
|
||||
attachments.data(),
|
||||
size.x,
|
||||
size.y,
|
||||
1U
|
||||
};
|
||||
|
||||
if (!framebuffers[i].Create(*m_device, frameBufferCreate))
|
||||
{
|
||||
NazaraError("Failed to create framebuffer for image #" + String::Number(i) + ": " + TranslateVulkanError(framebuffers[i].GetLastErrorCode()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_framebuffer.emplace(framebuffers.data(), framebuffers.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VkRenderWindow::SetupRenderPass()
|
||||
{
|
||||
std::array<VkAttachmentDescription, 2> attachments = {
|
||||
@@ -468,15 +554,31 @@ namespace Nz
|
||||
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
swapchainPresentMode,
|
||||
VK_TRUE,
|
||||
VK_NULL_HANDLE
|
||||
m_swapchain
|
||||
};
|
||||
|
||||
if (!m_swapchain.Create(*m_device, swapchainInfo))
|
||||
Vk::Swapchain newSwapchain;
|
||||
if (!newSwapchain.Create(*m_device, swapchainInfo))
|
||||
{
|
||||
NazaraError("Failed to create swapchain");
|
||||
NazaraError("failed to create swapchain: " + TranslateVulkanError(newSwapchain.GetLastErrorCode()));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_swapchain = std::move(newSwapchain);
|
||||
m_swapchainSize = size;
|
||||
|
||||
// Framebuffers
|
||||
m_inflightFences.resize(imageCount);
|
||||
|
||||
if (m_concurrentImageData.size() != imageCount)
|
||||
{
|
||||
m_concurrentImageData.clear();
|
||||
m_concurrentImageData.reserve(imageCount);
|
||||
|
||||
for (std::size_t i = 0; i < imageCount; ++i)
|
||||
m_concurrentImageData.emplace_back(*this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace Nz
|
||||
return std::make_unique<VulkanSurface>();
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderWindowImpl> VulkanRenderer::CreateRenderWindowImpl()
|
||||
std::unique_ptr<RenderWindowImpl> VulkanRenderer::CreateRenderWindowImpl(RenderWindow& owner)
|
||||
{
|
||||
return std::make_unique<VkRenderWindow>();
|
||||
return std::make_unique<VkRenderWindow>(owner);
|
||||
}
|
||||
|
||||
std::shared_ptr<RenderDevice> VulkanRenderer::InstanciateRenderDevice(std::size_t deviceIndex)
|
||||
|
||||
Reference in New Issue
Block a user