diff --git a/build/scripts/tools/openglrenderer.lua b/build/scripts/tools/openglrenderer.lua index 48b198327..2638de56d 100644 --- a/build/scripts/tools/openglrenderer.lua +++ b/build/scripts/tools/openglrenderer.lua @@ -26,6 +26,7 @@ TOOL.Files = { TOOL.Libraries = { "NazaraCore", + "NazaraPlatform", "NazaraRenderer", "NazaraShader", "NazaraUtility" diff --git a/build/scripts/tools/vulkanrenderer.lua b/build/scripts/tools/vulkanrenderer.lua index 4e7f2be10..5afe3f018 100644 --- a/build/scripts/tools/vulkanrenderer.lua +++ b/build/scripts/tools/vulkanrenderer.lua @@ -27,6 +27,7 @@ TOOL.Files = { TOOL.Libraries = { "NazaraCore", + "NazaraPlatform", "NazaraRenderer", "NazaraShader", "NazaraUtility" diff --git a/examples/VulkanTest/main.cpp b/examples/VulkanTest/main.cpp index 54c34762c..fc235ea8c 100644 --- a/examples/VulkanTest/main.cpp +++ b/examples/VulkanTest/main.cpp @@ -8,7 +8,7 @@ int main() Nz::Initializer 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 drawCommandBuffer = commandPool->BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder) + std::unique_ptr 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++; diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderWindow.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderWindow.hpp index ceb7b2489..9031a01d1 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderWindow.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderWindow.hpp @@ -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 CreateCommandPool(QueueType queueType) override; inline GL::Context& GetContext(); @@ -46,6 +48,7 @@ namespace Nz std::unique_ptr m_context; OpenGLRenderPass m_renderPass; OpenGLWindowFramebuffer m_framebuffer; + RenderWindow& m_owner; }; } diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp index 0fefae4de..4fb4b13ed 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp @@ -24,7 +24,7 @@ namespace Nz ~OpenGLRenderer(); std::unique_ptr CreateRenderSurfaceImpl() override; - std::unique_ptr CreateRenderWindowImpl() override; + std::unique_ptr CreateRenderWindowImpl(RenderWindow& owner) override; std::shared_ptr InstanciateRenderDevice(std::size_t deviceIndex) override; diff --git a/include/Nazara/Renderer/RenderFrame.hpp b/include/Nazara/Renderer/RenderFrame.hpp new file mode 100644 index 000000000..1e16bd10c --- /dev/null +++ b/include/Nazara/Renderer/RenderFrame.hpp @@ -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 +#include +#include +#include + +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& 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 + +#endif diff --git a/include/Nazara/Renderer/RenderFrame.inl b/include/Nazara/Renderer/RenderFrame.inl new file mode 100644 index 000000000..6a3cd4e19 --- /dev/null +++ b/include/Nazara/Renderer/RenderFrame.inl @@ -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 +#include + +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 diff --git a/include/Nazara/Renderer/RenderImage.hpp b/include/Nazara/Renderer/RenderImage.hpp index 1ab5310ff..3081ac231 100644 --- a/include/Nazara/Renderer/RenderImage.hpp +++ b/include/Nazara/Renderer/RenderImage.hpp @@ -21,7 +21,6 @@ namespace Nz class NAZARA_RENDERER_API RenderImage { public: - RenderImage() = default; virtual ~RenderImage(); virtual void Execute(const std::function& callback, QueueTypeFlags queueTypeFlags) = 0; @@ -33,6 +32,7 @@ namespace Nz virtual void Present() = 0; protected: + RenderImage() = default; RenderImage(const RenderImage&) = delete; RenderImage(RenderImage&&) = default; }; diff --git a/include/Nazara/Renderer/RenderWindow.hpp b/include/Nazara/Renderer/RenderWindow.hpp index 0d3521a00..6adcf3d66 100644 --- a/include/Nazara/Renderer/RenderWindow.hpp +++ b/include/Nazara/Renderer/RenderWindow.hpp @@ -36,8 +36,9 @@ namespace Nz void EnableVerticalSync(bool enabled); - inline RenderWindowImpl *GetImpl(); + inline RenderWindowImpl* GetImpl(); std::shared_ptr GetRenderDevice(); + inline RenderSurface* GetSurface(); inline bool IsValid() const; diff --git a/include/Nazara/Renderer/RenderWindow.inl b/include/Nazara/Renderer/RenderWindow.inl index 6b868a3b6..95b61dd8b 100644 --- a/include/Nazara/Renderer/RenderWindow.inl +++ b/include/Nazara/Renderer/RenderWindow.inl @@ -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; diff --git a/include/Nazara/Renderer/RenderWindowImpl.hpp b/include/Nazara/Renderer/RenderWindowImpl.hpp index eb6736b74..db8a15048 100644 --- a/include/Nazara/Renderer/RenderWindowImpl.hpp +++ b/include/Nazara/Renderer/RenderWindowImpl.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include 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 CreateCommandPool(QueueType queueType) = 0; virtual const Framebuffer& GetFramebuffer() const = 0; diff --git a/include/Nazara/Renderer/RendererImpl.hpp b/include/Nazara/Renderer/RendererImpl.hpp index 99aefe682..8bff2d8a2 100644 --- a/include/Nazara/Renderer/RendererImpl.hpp +++ b/include/Nazara/Renderer/RendererImpl.hpp @@ -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 CreateRenderSurfaceImpl() = 0; - virtual std::unique_ptr CreateRenderWindowImpl() = 0; + virtual std::unique_ptr CreateRenderWindowImpl(RenderWindow& owner) = 0; virtual std::shared_ptr InstanciateRenderDevice(std::size_t deviceIndex) = 0; diff --git a/include/Nazara/VulkanRenderer/VkRenderWindow.hpp b/include/Nazara/VulkanRenderer/VkRenderWindow.hpp index 3df9c1f66..6ad0eaae8 100644 --- a/include/Nazara/VulkanRenderer/VkRenderWindow.hpp +++ b/include/Nazara/VulkanRenderer/VkRenderWindow.hpp @@ -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 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 GetRenderDevice() override; + inline std::shared_ptr 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 m_framebuffer; std::optional m_renderPass; std::shared_ptr m_device; + std::size_t m_currentFrame; std::vector m_inflightFences; std::vector 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; }; } diff --git a/include/Nazara/VulkanRenderer/VkRenderWindow.inl b/include/Nazara/VulkanRenderer/VkRenderWindow.inl index dc6cf2206..5daff2463 100644 --- a/include/Nazara/VulkanRenderer/VkRenderWindow.inl +++ b/include/Nazara/VulkanRenderer/VkRenderWindow.inl @@ -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 Nz::VkRenderWindow::GetRenderDevice() + inline std::shared_ptr 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 diff --git a/include/Nazara/VulkanRenderer/VulkanRenderer.hpp b/include/Nazara/VulkanRenderer/VulkanRenderer.hpp index 8f6d66160..d0c14c6b5 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderer.hpp +++ b/include/Nazara/VulkanRenderer/VulkanRenderer.hpp @@ -26,7 +26,7 @@ namespace Nz ~VulkanRenderer(); std::unique_ptr CreateRenderSurfaceImpl() override; - std::unique_ptr CreateRenderWindowImpl() override; + std::unique_ptr CreateRenderWindowImpl(RenderWindow& owner) override; std::shared_ptr InstanciateRenderDevice(std::size_t deviceIndex) override; diff --git a/include/Nazara/VulkanRenderer/Wrapper/Swapchain.hpp b/include/Nazara/VulkanRenderer/Wrapper/Swapchain.hpp index 399ca909f..2cd24bb5b 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Swapchain.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/Swapchain.hpp @@ -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 { diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderWindow.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderWindow.cpp index 6ddb9f5bf..a4eaec923 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderWindow.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderWindow.cpp @@ -7,22 +7,24 @@ #include #include #include +#include #include 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(surface); OpenGLRenderer* glRenderer = static_cast(renderer); diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp index 3d6b6a109..e41eed2e8 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp @@ -31,9 +31,9 @@ namespace Nz return std::make_unique(); } - std::unique_ptr OpenGLRenderer::CreateRenderWindowImpl() + std::unique_ptr OpenGLRenderer::CreateRenderWindowImpl(RenderWindow& owner) { - return std::make_unique(); + return std::make_unique(owner); } std::shared_ptr OpenGLRenderer::InstanciateRenderDevice(std::size_t deviceIndex) diff --git a/src/Nazara/Renderer/RenderFrame.cpp b/src/Nazara/Renderer/RenderFrame.cpp new file mode 100644 index 000000000..485ba2aa1 --- /dev/null +++ b/src/Nazara/Renderer/RenderFrame.cpp @@ -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 +#include +#include +#include + +namespace Nz +{ + void RenderFrame::Execute(const std::function& 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; + } +} diff --git a/src/Nazara/Renderer/RenderWindow.cpp b/src/Nazara/Renderer/RenderWindow.cpp index a03cf295c..25b04b0dc 100644 --- a/src/Nazara/Renderer/RenderWindow.cpp +++ b/src/Nazara/Renderer/RenderWindow.cpp @@ -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; diff --git a/src/Nazara/VulkanRenderer/VkRenderWindow.cpp b/src/Nazara/VulkanRenderer/VkRenderWindow.cpp index bfba96bc1..ee83084d7 100644 --- a/src/Nazara/VulkanRenderer/VkRenderWindow.cpp +++ b/src/Nazara/VulkanRenderer/VkRenderWindow.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -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(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::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::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 framebuffers = NazaraStackArray(Vk::Framebuffer, imageCount); - for (UInt32 i = 0; i < imageCount; ++i) + if (!CreateSwapchain(vulkanSurface, m_owner.GetSize())) { - std::array 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(*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 framebuffers = NazaraStackArray(Vk::Framebuffer, imageCount); + for (UInt32 i = 0; i < imageCount; ++i) + { + std::array 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 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; } } diff --git a/src/Nazara/VulkanRenderer/VulkanRenderer.cpp b/src/Nazara/VulkanRenderer/VulkanRenderer.cpp index 9eed59b0c..aa0edcb5a 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderer.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderer.cpp @@ -25,9 +25,9 @@ namespace Nz return std::make_unique(); } - std::unique_ptr VulkanRenderer::CreateRenderWindowImpl() + std::unique_ptr VulkanRenderer::CreateRenderWindowImpl(RenderWindow& owner) { - return std::make_unique(); + return std::make_unique(owner); } std::shared_ptr VulkanRenderer::InstanciateRenderDevice(std::size_t deviceIndex)