VulkanRenderer: Handle window resize

This commit is contained in:
Jérôme Leclercq 2020-08-27 16:16:33 +02:00
parent 982d28cace
commit cbdac32f5f
22 changed files with 420 additions and 137 deletions

View File

@ -26,6 +26,7 @@ TOOL.Files = {
TOOL.Libraries = {
"NazaraCore",
"NazaraPlatform",
"NazaraRenderer",
"NazaraShader",
"NazaraUtility"

View File

@ -27,6 +27,7 @@ TOOL.Files = {
TOOL.Libraries = {
"NazaraCore",
"NazaraPlatform",
"NazaraRenderer",
"NazaraShader",
"NazaraUtility"

View File

@ -8,7 +8,7 @@ int main()
Nz::Initializer<Nz::Renderer> loader;
if (!loader)
{
std::cout << "Failed to initialize Vulkan" << std::endl;;
std::cout << "Failed to initialize Vulkan" << std::endl;
return __LINE__;
}
@ -172,33 +172,41 @@ int main()
Nz::AbstractBuffer* indexBufferImpl = renderBufferIB->GetHardwareBuffer(renderDevice);
Nz::AbstractBuffer* vertexBufferImpl = renderBufferVB->GetHardwareBuffer(renderDevice);
std::unique_ptr<Nz::CommandBuffer> drawCommandBuffer = commandPool->BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder)
std::unique_ptr<Nz::CommandBuffer> drawCommandBuffer;
auto RebuildCommandBuffer = [&]
{
Nz::Recti renderRect(0, 0, window.GetSize().x, window.GetSize().y);
Nz::Vector2ui windowSize = window.GetSize();
Nz::CommandBufferBuilder::ClearValues clearValues[2];
clearValues[0].color = Nz::Color::Black;
clearValues[1].depth = 1.f;
clearValues[1].stencil = 0;
builder.BeginDebugRegion("Main window rendering", Nz::Color::Green);
drawCommandBuffer = commandPool->BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder)
{
builder.BeginRenderPass(windowImpl->GetFramebuffer(), windowImpl->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] });
Nz::Recti renderRect(0, 0, window.GetSize().x, window.GetSize().y);
Nz::CommandBufferBuilder::ClearValues clearValues[2];
clearValues[0].color = Nz::Color::Black;
clearValues[1].depth = 1.f;
clearValues[1].stencil = 0;
builder.BeginDebugRegion("Main window rendering", Nz::Color::Green);
{
builder.BindIndexBuffer(indexBufferImpl);
builder.BindPipeline(*pipeline);
builder.BindVertexBuffer(0, vertexBufferImpl);
builder.BindShaderBinding(*shaderBinding);
builder.BeginRenderPass(windowImpl->GetFramebuffer(), windowImpl->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] });
{
builder.BindIndexBuffer(indexBufferImpl);
builder.BindPipeline(*pipeline);
builder.BindVertexBuffer(0, vertexBufferImpl);
builder.BindShaderBinding(*shaderBinding);
builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
builder.DrawIndexed(drfreakIB->GetIndexCount());
builder.DrawIndexed(drfreakIB->GetIndexCount());
}
builder.EndRenderPass();
}
builder.EndRenderPass();
}
builder.EndDebugRegion();
});
builder.EndDebugRegion();
});
};
RebuildCommandBuffer();
Nz::Vector3f viewerPos = Nz::Vector3f::Zero();
@ -210,6 +218,7 @@ int main()
Nz::Clock updateClock;
Nz::Clock secondClock;
unsigned int fps = 0;
bool uboUpdate = true;
Nz::Mouse::SetRelativeMouseMode(true);
@ -236,8 +245,20 @@ int main()
camAngles.pitch = Nz::Clamp(camAngles.pitch + event.mouseMove.deltaY*sensitivity, -89.f, 89.f);
camQuat = camAngles;
uboUpdate = true;
break;
case Nz::WindowEventType_Resized:
{
Nz::Vector2ui windowSize = window.GetSize();
ubo.projectionMatrix = Nz::Matrix4f::Perspective(70.f, float(windowSize.x) / windowSize.y, 0.1f, 1000.f);
uboUpdate = true;
break;
}
default:
break;
}
}
@ -268,30 +289,44 @@ int main()
// Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc...
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RControl))
viewerPos += Nz::Vector3f::Down() * cameraSpeed;
uboUpdate = true;
}
Nz::RenderImage& renderImage = windowImpl->Acquire();
Nz::RenderFrame frame = windowImpl->Acquire();
if (!frame)
continue;
if (frame.IsFramebufferInvalidated())
RebuildCommandBuffer();
ubo.viewMatrix = Nz::Matrix4f::ViewMatrix(viewerPos, camAngles);
auto& allocation = renderImage.GetUploadPool().Allocate(uniformSize);
std::memcpy(allocation.mappedPtr, &ubo, sizeof(ubo));
renderImage.Execute([&](Nz::CommandBufferBuilder& builder)
if (uboUpdate)
{
builder.BeginDebugRegion("UBO Update", Nz::Color::Yellow);
auto& allocation = frame.GetUploadPool().Allocate(uniformSize);
std::memcpy(allocation.mappedPtr, &ubo, sizeof(ubo));
frame.Execute([&](Nz::CommandBufferBuilder& builder)
{
builder.PreTransferBarrier();
builder.CopyBuffer(allocation, uniformBuffer.get());
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, Nz::QueueType::Transfer);
builder.BeginDebugRegion("UBO Update", Nz::Color::Yellow);
{
builder.PreTransferBarrier();
builder.CopyBuffer(allocation, uniformBuffer.get());
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, Nz::QueueType::Transfer);
renderImage.SubmitCommandBuffer(drawCommandBuffer.get(), Nz::QueueType::Graphics);
uboUpdate = false;
}
renderImage.Present();
frame.SubmitCommandBuffer(drawCommandBuffer.get(), Nz::QueueType::Graphics);
frame.Present();
window.Display();
// On incrémente le compteur de FPS improvisé
fps++;

View File

@ -20,15 +20,17 @@
namespace Nz
{
class RenderWindow;
class NAZARA_OPENGLRENDERER_API OpenGLRenderWindow : public RenderWindowImpl
{
public:
OpenGLRenderWindow();
OpenGLRenderWindow(RenderWindow& owner);
~OpenGLRenderWindow() = default;
OpenGLRenderImage& Acquire() override;
RenderFrame Acquire() override;
bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) override;
bool Create(RendererImpl* renderer, RenderSurface* surface, const RenderWindowParameters& parameters) override;
std::unique_ptr<CommandPool> CreateCommandPool(QueueType queueType) override;
inline GL::Context& GetContext();
@ -46,6 +48,7 @@ namespace Nz
std::unique_ptr<GL::Context> m_context;
OpenGLRenderPass m_renderPass;
OpenGLWindowFramebuffer m_framebuffer;
RenderWindow& m_owner;
};
}

View File

@ -24,7 +24,7 @@ namespace Nz
~OpenGLRenderer();
std::unique_ptr<RenderSurface> CreateRenderSurfaceImpl() override;
std::unique_ptr<RenderWindowImpl> CreateRenderWindowImpl() override;
std::unique_ptr<RenderWindowImpl> CreateRenderWindowImpl(RenderWindow& owner) override;
std::shared_ptr<RenderDevice> InstanciateRenderDevice(std::size_t deviceIndex) override;

View File

@ -0,0 +1,54 @@
// 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
#pragma once
#ifndef NAZARA_RENDERFRAME_HPP
#define NAZARA_RENDERFRAME_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <functional>
namespace Nz
{
class CommandBuffer;
class CommandBufferBuilder;
class RenderImage;
class UploadPool;
class NAZARA_RENDERER_API RenderFrame
{
public:
inline explicit RenderFrame();
inline explicit RenderFrame(RenderImage* renderImage, bool framebufferInvalidation);
RenderFrame(const RenderFrame&) = delete;
RenderFrame(RenderFrame&&) = delete;
~RenderFrame() = default;
void Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags);
UploadPool& GetUploadPool();
inline bool IsFramebufferInvalidated() const;
void SubmitCommandBuffer(CommandBuffer* commandBuffer, QueueTypeFlags queueTypeFlags) ;
void Present();
inline explicit operator bool();
RenderFrame& operator=(const RenderFrame&) = delete;
RenderFrame& operator=(RenderFrame&&) = delete;
private:
RenderImage* m_image;
bool m_framebufferInvalidation;
};
}
#include <Nazara/Renderer/RenderFrame.inl>
#endif

View File

@ -0,0 +1,32 @@
// 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/Debug.hpp>
namespace Nz
{
inline RenderFrame::RenderFrame() :
RenderFrame(nullptr, false)
{
}
inline RenderFrame::RenderFrame(RenderImage* renderImage, bool framebufferInvalidation) :
m_image(renderImage),
m_framebufferInvalidation(framebufferInvalidation)
{
}
inline bool RenderFrame::IsFramebufferInvalidated() const
{
return m_framebufferInvalidation;
}
inline RenderFrame::operator bool()
{
return m_image != nullptr;
}
}
#include <Nazara/Renderer/DebugOff.hpp>

View File

@ -21,7 +21,6 @@ namespace Nz
class NAZARA_RENDERER_API RenderImage
{
public:
RenderImage() = default;
virtual ~RenderImage();
virtual void Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags) = 0;
@ -33,6 +32,7 @@ namespace Nz
virtual void Present() = 0;
protected:
RenderImage() = default;
RenderImage(const RenderImage&) = delete;
RenderImage(RenderImage&&) = default;
};

View File

@ -36,8 +36,9 @@ namespace Nz
void EnableVerticalSync(bool enabled);
inline RenderWindowImpl *GetImpl();
inline RenderWindowImpl* GetImpl();
std::shared_ptr<RenderDevice> GetRenderDevice();
inline RenderSurface* GetSurface();
inline bool IsValid() const;

View File

@ -46,11 +46,16 @@ namespace Nz
return Window::Create(handle);
}
inline RenderWindowImpl* Nz::RenderWindow::GetImpl()
inline RenderWindowImpl* RenderWindow::GetImpl()
{
return m_impl.get();
}
inline RenderSurface* RenderWindow::GetSurface()
{
return m_surface.get();
}
inline bool RenderWindow::IsValid() const
{
return m_impl != nullptr;

View File

@ -12,6 +12,7 @@
#include <Nazara/Platform/WindowHandle.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
#include <Nazara/Renderer/RenderWindowParameters.hpp>
namespace Nz
@ -19,7 +20,6 @@ namespace Nz
class CommandPool;
class Framebuffer;
class RendererImpl;
class RenderImage;
class RenderPass;
class RenderSurface;
@ -29,9 +29,9 @@ namespace Nz
RenderWindowImpl() = default;
virtual ~RenderWindowImpl();
virtual RenderImage& Acquire() = 0;
virtual RenderFrame Acquire() = 0;
virtual bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) = 0;
virtual bool Create(RendererImpl* renderer, RenderSurface* surface, const RenderWindowParameters& parameters) = 0;
virtual std::unique_ptr<CommandPool> CreateCommandPool(QueueType queueType) = 0;
virtual const Framebuffer& GetFramebuffer() const = 0;

View File

@ -23,6 +23,7 @@ namespace Nz
class RendererImpl;
class RenderDevice;
class RenderSurface;
class RenderWindow;
class RenderWindowImpl;
using CreateRendererImplFunc = RendererImpl*(*)();
@ -34,7 +35,7 @@ namespace Nz
virtual ~RendererImpl();
virtual std::unique_ptr<RenderSurface> CreateRenderSurfaceImpl() = 0;
virtual std::unique_ptr<RenderWindowImpl> CreateRenderWindowImpl() = 0;
virtual std::unique_ptr<RenderWindowImpl> CreateRenderWindowImpl(RenderWindow& owner) = 0;
virtual std::shared_ptr<RenderDevice> InstanciateRenderDevice(std::size_t deviceIndex) = 0;

View File

@ -37,24 +37,25 @@ namespace Nz
class NAZARA_VULKANRENDERER_API VkRenderWindow : public VkRenderTarget, public RenderWindowImpl
{
public:
VkRenderWindow();
VkRenderWindow(RenderWindow& owner);
VkRenderWindow(const VkRenderWindow&) = delete;
VkRenderWindow(VkRenderWindow&&) = delete; ///TODO
~VkRenderWindow();
VulkanRenderImage& Acquire() override;
RenderFrame Acquire() override;
bool Create(RendererImpl* renderer, RenderSurface* surface, const RenderWindowParameters& parameters) override;
bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) override;
std::unique_ptr<CommandPool> CreateCommandPool(QueueType queueType) override;
inline const VulkanMultipleFramebuffer& GetFramebuffer() const override;
inline VulkanDevice& GetDevice();
inline const VulkanDevice& GetDevice() const;
inline Vk::QueueHandle& GetGraphicsQueue();
const VulkanRenderPass& GetRenderPass() const override;
inline const VulkanRenderPass& GetRenderPass() const override;
inline const Vk::Swapchain& GetSwapchain() const;
std::shared_ptr<RenderDevice> GetRenderDevice() override;
inline std::shared_ptr<RenderDevice> GetRenderDevice() override;
void Present(UInt32 imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE);
@ -62,17 +63,16 @@ namespace Nz
VkRenderWindow& operator=(VkRenderWindow&&) = delete; ///TODO
private:
bool CreateSwapchain(Vk::Surface& surface, const Vector2ui& size);
bool SetupDepthBuffer(const Vector2ui& size);
bool SetupFrameBuffers(const Vector2ui& size);
bool SetupRenderPass();
bool SetupSwapchain(const Vk::PhysicalDevice& deviceInfo, Vk::Surface& surface, const Vector2ui& size);
std::size_t m_currentFrame;
Clock m_clock;
VkFormat m_depthStencilFormat;
VkSurfaceFormatKHR m_surfaceFormat;
std::optional<VulkanMultipleFramebuffer> m_framebuffer;
std::optional<VulkanRenderPass> m_renderPass;
std::shared_ptr<VulkanDevice> m_device;
std::size_t m_currentFrame;
std::vector<Vk::Fence*> m_inflightFences;
std::vector<VulkanRenderImage> m_concurrentImageData;
Vk::DeviceMemory m_depthBufferMemory;
@ -82,6 +82,12 @@ namespace Nz
Vk::QueueHandle m_presentQueue;
Vk::QueueHandle m_transferQueue;
Vk::Swapchain m_swapchain;
Clock m_clock;
RenderWindow& m_owner;
Vector2ui m_swapchainSize;
VkFormat m_depthStencilFormat;
VkSurfaceFormatKHR m_surfaceFormat;
bool m_shouldRecreateSwapchain;
};
}

View File

@ -27,24 +27,20 @@ namespace Nz
return m_graphicsQueue;
}
inline const VulkanRenderPass& VkRenderWindow::GetRenderPass() const
{
return *m_renderPass;
}
inline const Vk::Swapchain& VkRenderWindow::GetSwapchain() const
{
return m_swapchain;
}
inline std::shared_ptr<RenderDevice> Nz::VkRenderWindow::GetRenderDevice()
inline std::shared_ptr<RenderDevice> VkRenderWindow::GetRenderDevice()
{
return m_device;
}
inline void VkRenderWindow::Present(UInt32 imageIndex, VkSemaphore waitSemaphore)
{
NazaraAssert(imageIndex < m_inflightFences.size(), "Invalid image index");
m_presentQueue.Present(m_swapchain, imageIndex, waitSemaphore);
m_currentFrame = (m_currentFrame + 1) % m_inflightFences.size();
}
}
#include <Nazara/VulkanRenderer/DebugOff.hpp>

View File

@ -26,7 +26,7 @@ namespace Nz
~VulkanRenderer();
std::unique_ptr<RenderSurface> CreateRenderSurfaceImpl() override;
std::unique_ptr<RenderWindowImpl> CreateRenderWindowImpl() override;
std::unique_ptr<RenderWindowImpl> CreateRenderWindowImpl(RenderWindow& owner) override;
std::shared_ptr<RenderDevice> InstanciateRenderDevice(std::size_t deviceIndex) override;

View File

@ -38,7 +38,7 @@ namespace Nz
inline bool IsSupported() const;
Swapchain& operator=(const Swapchain&) = delete;
Swapchain& operator=(Swapchain&&) = delete;
Swapchain& operator=(Swapchain&&) = default;
struct Buffer
{

View File

@ -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);

View File

@ -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)

View 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;
}
}

View File

@ -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;

View File

@ -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(&currentFrame, 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;
}
}

View File

@ -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)