From aaf3d97954d42e2ad2d81985f2cae8e894a72100 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Mon, 20 Nov 2023 00:04:11 +0100 Subject: [PATCH] Graphics: Add RenderTexture class --- assets/examples_version.txt | 2 +- examples/DeferredShading/main.cpp | 8 +- examples/Showcase/main.cpp | 75 +++++++++++++++ include/Nazara/Core.hpp | 1 + include/Nazara/Graphics.hpp | 6 ++ include/Nazara/Graphics/BakedFrameGraph.hpp | 2 +- include/Nazara/Graphics/Camera.hpp | 2 +- include/Nazara/Graphics/FrameGraph.hpp | 5 +- include/Nazara/Graphics/FrameGraph.inl | 10 +- include/Nazara/Graphics/FrameGraphStructs.hpp | 2 + include/Nazara/Graphics/FramePass.hpp | 2 +- .../Nazara/Graphics/FramePassAttachment.hpp | 8 +- include/Nazara/Graphics/PipelinePassList.hpp | 2 +- include/Nazara/Graphics/PipelineViewer.hpp | 2 +- .../OpenGLRenderer/OpenGLCommandBuffer.inl | 1 - include/Nazara/Renderer.hpp | 1 + include/Nazara/Renderer/RenderTarget.hpp | 3 - include/Nazara/Renderer/RenderTexture.hpp | 46 +++++++++ include/Nazara/Renderer/RenderTexture.inl | 16 ++++ include/Nazara/Renderer/Swapchain.hpp | 3 + .../Nazara/Renderer/TransientResources.inl | 1 - include/Nazara/Renderer/WindowSwapchain.hpp | 6 +- include/Nazara/Renderer/WindowSwapchain.inl | 18 ++++ src/Nazara/Graphics/BakedFrameGraph.cpp | 7 ++ src/Nazara/Graphics/Camera.cpp | 4 +- src/Nazara/Graphics/ForwardFramePipeline.cpp | 22 ++++- src/Nazara/Graphics/FrameGraph.cpp | 93 ++++++++++++++----- src/Nazara/Graphics/PipelinePassList.cpp | 16 +++- src/Nazara/Renderer/RenderTexture.cpp | 45 +++++++++ src/Nazara/Renderer/WindowSwapchain.cpp | 15 --- tests/ComputeTest/main.cpp | 2 +- tests/RenderTest/main.cpp | 4 +- 32 files changed, 354 insertions(+), 76 deletions(-) create mode 100644 include/Nazara/Renderer/RenderTexture.hpp create mode 100644 include/Nazara/Renderer/RenderTexture.inl create mode 100644 src/Nazara/Renderer/RenderTexture.cpp diff --git a/assets/examples_version.txt b/assets/examples_version.txt index b8626c4cf..7ed6ff82d 100644 --- a/assets/examples_version.txt +++ b/assets/examples_version.txt @@ -1 +1 @@ -4 +5 diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 961477e84..7f6414e3f 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -1037,7 +1037,7 @@ int main(int argc, char* argv[]) builder.Draw(3); }); - graph.AddBackbufferOutput(toneMappingOutput); + graph.MarkAsFinalOutput(toneMappingOutput); return graph.Bake(); }(); @@ -1180,7 +1180,9 @@ int main(int argc, char* argv[]) currentFrame = &frame; - if (bakedGraph.Resize(frame)) + std::array sizes = { currentFrame->GetSize() }; + + if (bakedGraph.Resize(frame, sizes)) { frame.PushForRelease(std::move(gbufferShaderBinding)); @@ -1512,7 +1514,7 @@ int main(int argc, char* argv[]) bakedGraph.Execute(frame); - const Nz::RenderTarget* windowRT = &windowSwapchain; + const Nz::WindowSwapchain* windowRT = &windowSwapchain; frame.Execute([&](Nz::CommandBufferBuilder& builder) { Nz::Recti windowRenderRect(0, 0, window.GetSize().x, window.GetSize().y); diff --git a/examples/Showcase/main.cpp b/examples/Showcase/main.cpp index 6cb2421db..9e8b965cd 100644 --- a/examples/Showcase/main.cpp +++ b/examples/Showcase/main.cpp @@ -412,6 +412,81 @@ int main(int argc, char* argv[]) } } + Nz::TextureInfo screenTextureInfo = { + .pixelFormat = Nz::PixelFormat::RGBA8, + .type = Nz::ImageType::E2D, + .levelCount = 1, + .height = 360, + .width = 480 + }; + + std::shared_ptr screenTexture = device->InstantiateTexture(screenTextureInfo); + + Nz::RenderTexture renderTexture(screenTexture); + + entt::handle tvCameraEntity = world.CreateEntity(); + { + auto& cameraNode = tvCameraEntity.emplace(); + cameraNode.SetParentJoint(bobEntity, "Head"); + cameraNode.SetRotation(Nz::EulerAnglesf(-30.f, 180.f, 0.f)); + //cameraNode.SetParent(playerCamera); + + auto& cameraComponent = tvCameraEntity.emplace(&renderTexture); + cameraComponent.UpdateZNear(0.2f); + cameraComponent.UpdateZFar(1000.f); + cameraComponent.UpdateRenderMask(1); + cameraComponent.UpdateClearColor(Nz::Color(0.f, 0.f, 0.f)); + cameraComponent.UpdateRenderOrder(-1); + } + + + entt::handle tvEntity = world.CreateEntity(); + { + Nz::MeshParams tvMeshParams; + tvMeshParams.vertexScale = Nz::Vector3f(0.01f); + + std::shared_ptr tvMesh = fs.Load("assets/retro_tv_lowpoly_4k_textures/scene.gltf", tvMeshParams); + if (!tvMesh) + { + NazaraError("failed to load tv mesh"); + return __LINE__; + } + + tvMesh->RemoveSubMesh(1); // looks like this submesh has issues loading + + // Screen UVs are upside down (model made for OpenGL?) + { + // Reverse them + Nz::VertexMapper vertexMapper(*tvMesh->GetSubMesh(1)); + Nz::SparsePtr uv = vertexMapper.GetComponentPtr(Nz::VertexComponent::TexCoord); + for (Nz::UInt32 i = 0; i < vertexMapper.GetVertexCount(); ++i) + uv[i].y = 1.f - uv[i].y; + } + + std::shared_ptr tvGfxMesh = Nz::GraphicalMesh::BuildFromMesh(*tvMesh); + std::shared_ptr tvModel = std::make_shared(std::move(tvGfxMesh)); + + Nz::TextureParams srgbTexParams; + srgbTexParams.loadFormat = Nz::PixelFormat::RGBA8_SRGB; + + std::shared_ptr tvMat = Nz::MaterialInstance::Instantiate(Nz::MaterialType::PhysicallyBased); + tvMat->SetTextureProperty("BaseColorMap", fs.Load("assets/retro_tv_lowpoly_4k_textures/textures/old_tv_baseColor.jpeg", srgbTexParams)); + tvMat->SetTextureProperty("NormalMap", fs.Load("assets/retro_tv_lowpoly_4k_textures/textures/old_tv_normal.png")); + + tvModel->SetMaterial(0, tvMat); + + std::shared_ptr screenMat = Nz::MaterialInstance::Instantiate(Nz::MaterialType::PhysicallyBased); + screenMat->SetTextureProperty("BaseColorMap", screenTexture); + + tvModel->SetMaterial(1, screenMat); + + auto& tvNode = tvEntity.emplace(); + tvNode.SetPosition(Nz::Vector3f(-2.586f, 0.5f, 0.892f)); + + auto& tvGfx = tvEntity.emplace(); + tvGfx.AttachRenderable(tvModel); + } + Nz::MillisecondClock fpsClock, updateClock; float incr = 0.f; unsigned int currentFrame = 0; diff --git a/include/Nazara/Core.hpp b/include/Nazara/Core.hpp index 972663fc0..2dc121c0e 100644 --- a/include/Nazara/Core.hpp +++ b/include/Nazara/Core.hpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index ec4a887f9..05375d4c5 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -62,9 +64,13 @@ #include #include #include +#include +#include #include #include +#include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/BakedFrameGraph.hpp b/include/Nazara/Graphics/BakedFrameGraph.hpp index ea9070fc1..5cbf7712f 100644 --- a/include/Nazara/Graphics/BakedFrameGraph.hpp +++ b/include/Nazara/Graphics/BakedFrameGraph.hpp @@ -39,7 +39,7 @@ namespace Nz const std::shared_ptr& GetAttachmentTexture(std::size_t attachmentIndex) const; const std::shared_ptr& GetRenderPass(std::size_t passIndex) const; - bool Resize(RenderFrame& renderFrame); + bool Resize(RenderFrame& renderFrame, std::span viewerTargetSizes); BakedFrameGraph& operator=(const BakedFrameGraph&) = delete; BakedFrameGraph& operator=(BakedFrameGraph&&) noexcept = default; diff --git a/include/Nazara/Graphics/Camera.hpp b/include/Nazara/Graphics/Camera.hpp index 268f2ffc5..b8375e2a6 100644 --- a/include/Nazara/Graphics/Camera.hpp +++ b/include/Nazara/Graphics/Camera.hpp @@ -45,7 +45,7 @@ namespace Nz inline float GetZFar() const; inline float GetZNear() const; - std::size_t RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, const FunctionRef& passCallback = nullptr) const override; + std::size_t RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, std::optional viewerIndex, const FunctionRef& passCallback = nullptr) const override; inline void UpdateClearColor(Color color); inline void UpdateFOV(DegreeAnglef fov); diff --git a/include/Nazara/Graphics/FrameGraph.hpp b/include/Nazara/Graphics/FrameGraph.hpp index 44c2d295e..f2e83e9f7 100644 --- a/include/Nazara/Graphics/FrameGraph.hpp +++ b/include/Nazara/Graphics/FrameGraph.hpp @@ -38,11 +38,12 @@ namespace Nz inline std::size_t AddAttachmentCube(FramePassAttachment attachment); inline std::size_t AddAttachmentCubeFace(std::size_t attachmentId, CubemapFace face); inline std::size_t AddAttachmentProxy(std::string name, std::size_t attachmentId); - inline void AddBackbufferOutput(std::size_t backbufferOutput); inline FramePass& AddPass(std::string name); BakedFrameGraph Bake(); + inline void MarkAsFinalOutput(std::size_t attachmentIndex); + FrameGraph& operator=(const FrameGraph&) = delete; FrameGraph& operator=(FrameGraph&&) noexcept = default; @@ -138,7 +139,7 @@ namespace Nz using AttachmentType = std::variant; - std::vector m_backbufferOutputs; + std::vector m_finalOutputs; std::vector m_framePasses; std::vector m_attachments; WorkData m_pending; diff --git a/include/Nazara/Graphics/FrameGraph.inl b/include/Nazara/Graphics/FrameGraph.inl index e44e11bf8..06c5d4862 100644 --- a/include/Nazara/Graphics/FrameGraph.inl +++ b/include/Nazara/Graphics/FrameGraph.inl @@ -78,16 +78,16 @@ namespace Nz return id; } - inline void FrameGraph::AddBackbufferOutput(std::size_t backbufferOutput) - { - m_backbufferOutputs.push_back(backbufferOutput); - } - inline FramePass& FrameGraph::AddPass(std::string name) { std::size_t id = m_framePasses.size(); return m_framePasses.emplace_back(*this, id, std::move(name)); } + + inline void FrameGraph::MarkAsFinalOutput(std::size_t attachmentIndex) + { + m_finalOutputs.push_back(attachmentIndex); + } } #include diff --git a/include/Nazara/Graphics/FrameGraphStructs.hpp b/include/Nazara/Graphics/FrameGraphStructs.hpp index 4750aca7a..b9c0db3da 100644 --- a/include/Nazara/Graphics/FrameGraphStructs.hpp +++ b/include/Nazara/Graphics/FrameGraphStructs.hpp @@ -30,9 +30,11 @@ namespace Nz PixelFormat format; FramePassAttachmentSize size; TextureUsageFlags usage; + bool canReuse; unsigned int width; unsigned int height; unsigned int layerCount; + unsigned int viewerIndex; }; } diff --git a/include/Nazara/Graphics/FramePass.hpp b/include/Nazara/Graphics/FramePass.hpp index 3546ca20d..39ef2c5b3 100644 --- a/include/Nazara/Graphics/FramePass.hpp +++ b/include/Nazara/Graphics/FramePass.hpp @@ -90,8 +90,8 @@ namespace Nz struct Input { - std::size_t attachmentId; std::optional assumedLayout; + std::size_t attachmentId; bool doesRead = true; }; diff --git a/include/Nazara/Graphics/FramePassAttachment.hpp b/include/Nazara/Graphics/FramePassAttachment.hpp index 040fe2022..7fdfca9b2 100644 --- a/include/Nazara/Graphics/FramePassAttachment.hpp +++ b/include/Nazara/Graphics/FramePassAttachment.hpp @@ -18,17 +18,19 @@ namespace Nz enum class FramePassAttachmentSize { Fixed, - SwapchainFactor + SwapchainFactor, + ViewerTargetFactor, }; struct FramePassAttachment { std::string name; PixelFormat format; - TextureUsage additionalUsage = TextureUsage::TransferSource; - FramePassAttachmentSize size = FramePassAttachmentSize::SwapchainFactor; + TextureUsageFlags additionalUsages; + FramePassAttachmentSize size = FramePassAttachmentSize::ViewerTargetFactor; unsigned int width = 100'000; unsigned int height = 100'000; + unsigned int viewerIndex = 0; }; } diff --git a/include/Nazara/Graphics/PipelinePassList.hpp b/include/Nazara/Graphics/PipelinePassList.hpp index a098c7583..98ca194ac 100644 --- a/include/Nazara/Graphics/PipelinePassList.hpp +++ b/include/Nazara/Graphics/PipelinePassList.hpp @@ -53,7 +53,7 @@ namespace Nz inline void EnablePassFlags(std::size_t passIndex, FramePipelinePassFlags flags); - std::size_t RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, const FunctionRef& passCallback = nullptr) const; + std::size_t RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, std::optional viewerIndex, const FunctionRef& passCallback = nullptr) const; inline void SetFinalOutput(std::size_t attachmentIndex); diff --git a/include/Nazara/Graphics/PipelineViewer.hpp b/include/Nazara/Graphics/PipelineViewer.hpp index ca8e3349c..cccabaf52 100644 --- a/include/Nazara/Graphics/PipelineViewer.hpp +++ b/include/Nazara/Graphics/PipelineViewer.hpp @@ -28,7 +28,7 @@ namespace Nz virtual std::vector> BuildPasses(FramePipelinePass::PassData& passData) const = 0; - virtual std::size_t RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, const FunctionRef& passCallback = nullptr) const = 0; + virtual std::size_t RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, std::optional viewerIndex, const FunctionRef& passCallback = nullptr) const = 0; PipelineViewer& operator=(const PipelineViewer&) = delete; PipelineViewer& operator=(PipelineViewer&&) = delete; diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl index f4f65a9f4..cbb3c8555 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -270,4 +270,3 @@ namespace Nz } #include -#include "OpenGLCommandBuffer.hpp" diff --git a/include/Nazara/Renderer.hpp b/include/Nazara/Renderer.hpp index e82769b88..7b48d7bf9 100644 --- a/include/Nazara/Renderer.hpp +++ b/include/Nazara/Renderer.hpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Renderer/RenderTarget.hpp b/include/Nazara/Renderer/RenderTarget.hpp index 86fbec65f..1d4243d6b 100644 --- a/include/Nazara/Renderer/RenderTarget.hpp +++ b/include/Nazara/Renderer/RenderTarget.hpp @@ -28,9 +28,6 @@ namespace Nz virtual void BlitTexture(RenderFrame& renderFrame, CommandBufferBuilder& builder, const Texture& texture) const = 0; - virtual const Framebuffer& GetFramebuffer(std::size_t i) const = 0; - virtual std::size_t GetFramebufferCount() const = 0; - virtual const RenderPass& GetRenderPass() const = 0; virtual const Vector2ui& GetSize() const = 0; NazaraSignal(OnRenderTargetRelease, const RenderTarget* /*renderTarget*/); diff --git a/include/Nazara/Renderer/RenderTexture.hpp b/include/Nazara/Renderer/RenderTexture.hpp new file mode 100644 index 000000000..a13a9e954 --- /dev/null +++ b/include/Nazara/Renderer/RenderTexture.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// 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_RENDERER_RENDERTEXTURE_HPP +#define NAZARA_RENDERER_RENDERTEXTURE_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class Texture; + + class NAZARA_RENDERER_API RenderTexture : public RenderTarget + { + public: + inline RenderTexture(std::shared_ptr targetTexture); + inline RenderTexture(std::shared_ptr targetTexture, PipelineStage targetPipelineStage, MemoryAccessFlags targetMemoryFlags, TextureLayout targetLayout); + RenderTexture(const RenderTexture&) = delete; + RenderTexture(RenderTexture&&) = delete; + ~RenderTexture() = default; + + void BlitTexture(RenderFrame& renderFrame, CommandBufferBuilder& builder, const Texture& texture) const override; + + const Vector2ui& GetSize() const override; + + RenderTexture& operator=(const RenderTexture&) = delete; + RenderTexture& operator=(RenderTexture&&) = delete; + + private: + std::shared_ptr m_targetTexture; + MemoryAccessFlags m_targetMemoryFlags; + PipelineStage m_targetPipelineStage; + TextureLayout m_targetLayout; + Vector2ui m_textureSize; + }; +} + +#include + +#endif // NAZARA_RENDERER_RENDERTEXTURE_HPP diff --git a/include/Nazara/Renderer/RenderTexture.inl b/include/Nazara/Renderer/RenderTexture.inl new file mode 100644 index 000000000..e5796b327 --- /dev/null +++ b/include/Nazara/Renderer/RenderTexture.inl @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// 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 RenderTexture::RenderTexture(std::shared_ptr targetTexture) : + RenderTexture(std::move(targetTexture), PipelineStage::FragmentShader, MemoryAccess::ColorRead, TextureLayout::ColorInput) + { + } +} + +#include diff --git a/include/Nazara/Renderer/Swapchain.hpp b/include/Nazara/Renderer/Swapchain.hpp index 756535920..e8c064800 100644 --- a/include/Nazara/Renderer/Swapchain.hpp +++ b/include/Nazara/Renderer/Swapchain.hpp @@ -33,7 +33,10 @@ namespace Nz virtual std::shared_ptr CreateCommandPool(QueueType queueType) = 0; + virtual const Framebuffer& GetFramebuffer(std::size_t i) const = 0; + virtual std::size_t GetFramebufferCount() const = 0; virtual PresentMode GetPresentMode() const = 0; + virtual const RenderPass& GetRenderPass() const = 0; virtual PresentModeFlags GetSupportedPresentModes() const = 0; virtual void NotifyResize(const Vector2ui& newSize) = 0; diff --git a/include/Nazara/Renderer/TransientResources.inl b/include/Nazara/Renderer/TransientResources.inl index e8350de73..657183642 100644 --- a/include/Nazara/Renderer/TransientResources.inl +++ b/include/Nazara/Renderer/TransientResources.inl @@ -108,4 +108,3 @@ namespace Nz } #include -#include "TransientResources.hpp" diff --git a/include/Nazara/Renderer/WindowSwapchain.hpp b/include/Nazara/Renderer/WindowSwapchain.hpp index 00ece75a5..dae809996 100644 --- a/include/Nazara/Renderer/WindowSwapchain.hpp +++ b/include/Nazara/Renderer/WindowSwapchain.hpp @@ -35,9 +35,9 @@ namespace Nz inline void EnableRenderOnlyIfFocused(bool enable = true); - const Framebuffer& GetFramebuffer(std::size_t i) const override; - std::size_t GetFramebufferCount() const override; - const RenderPass& GetRenderPass() const override; + inline const Framebuffer& GetFramebuffer(std::size_t i) const; + inline std::size_t GetFramebufferCount() const; + inline const RenderPass& GetRenderPass() const; const Vector2ui& GetSize() const override; inline Swapchain& GetSwapchain(); inline const Swapchain& GetSwapchain() const; diff --git a/include/Nazara/Renderer/WindowSwapchain.inl b/include/Nazara/Renderer/WindowSwapchain.inl index 35a3dfbbc..4028b0fac 100644 --- a/include/Nazara/Renderer/WindowSwapchain.inl +++ b/include/Nazara/Renderer/WindowSwapchain.inl @@ -35,6 +35,24 @@ namespace Nz m_renderOnlyIfFocused = enable; } + inline const Framebuffer& WindowSwapchain::GetFramebuffer(std::size_t i) const + { + assert(m_swapchain); + return m_swapchain->GetFramebuffer(i); + } + + inline std::size_t WindowSwapchain::GetFramebufferCount() const + { + assert(m_swapchain); + return m_swapchain->GetFramebufferCount(); + } + + inline const RenderPass& WindowSwapchain::GetRenderPass() const + { + assert(m_swapchain); + return m_swapchain->GetRenderPass(); + } + inline Swapchain& WindowSwapchain::GetSwapchain() { return *m_swapchain; diff --git a/src/Nazara/Graphics/BakedFrameGraph.cpp b/src/Nazara/Graphics/BakedFrameGraph.cpp index 3f38d1cd4..fa3b47ec7 100644 --- a/src/Nazara/Graphics/BakedFrameGraph.cpp +++ b/src/Nazara/Graphics/BakedFrameGraph.cpp @@ -153,6 +153,13 @@ namespace Nz texDimensions.y *= textureData.height; texDimensions /= 100'000; break; + + case FramePassAttachmentSize::ViewerTargetFactor: + texDimensions = viewerTargetSizes[textureData.viewerIndex]; + texDimensions.x *= textureData.width; + texDimensions.y *= textureData.height; + texDimensions /= 100'000; + break; } return texDimensions; diff --git a/src/Nazara/Graphics/Camera.cpp b/src/Nazara/Graphics/Camera.cpp index 5ed16dbd4..471d7b2f5 100644 --- a/src/Nazara/Graphics/Camera.cpp +++ b/src/Nazara/Graphics/Camera.cpp @@ -55,10 +55,10 @@ namespace Nz return m_viewport; } - std::size_t Camera::RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, const FunctionRef& passCallback) const + std::size_t Camera::RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, std::optional viewerIndex, const FunctionRef& passCallback) const { assert(m_framePipelinePasses); - return m_framePipelinePasses->RegisterPasses(passes, frameGraph, passCallback); + return m_framePipelinePasses->RegisterPasses(passes, frameGraph, viewerIndex, passCallback); } void Camera::UpdateTarget(const RenderTarget* renderTarget) diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index a938da6ec..5d368a651 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -368,16 +369,26 @@ namespace Nz } m_removedWorldInstances.Clear(); + StackVector viewerSizes = NazaraStackVector(Vector2ui, m_viewerPool.size()); + for (auto& viewerData : m_viewerPool) + { + if (viewerData.pendingDestruction) + continue; + + Recti viewport = viewerData.viewer->GetViewport(); + viewerSizes.emplace_back(Vector2i(viewport.width, viewport.height)); + } + bool frameGraphInvalidated; if (m_rebuildFrameGraph) { renderFrame.PushForRelease(std::move(m_bakedFrameGraph)); m_bakedFrameGraph = BuildFrameGraph(renderFrame); - m_bakedFrameGraph.Resize(renderFrame); + m_bakedFrameGraph.Resize(renderFrame, viewerSizes); frameGraphInvalidated = true; } else - frameGraphInvalidated = m_bakedFrameGraph.Resize(renderFrame); + frameGraphInvalidated = m_bakedFrameGraph.Resize(renderFrame, viewerSizes); // Find active lights (i.e. visible in any frustum) m_activeLights.Clear(); @@ -658,6 +669,7 @@ namespace Nz lightData->shadowData->RegisterToFrameGraph(frameGraph, nullptr); } + unsigned int viewerIndex = 0; for (auto& viewerData : m_viewerPool) { if (viewerData.pendingDestruction) @@ -684,7 +696,7 @@ namespace Nz } }; - viewerData.finalColorAttachment = viewerData.viewer->RegisterPasses(viewerData.passes, frameGraph, framePassCallback); + viewerData.finalColorAttachment = viewerData.viewer->RegisterPasses(viewerData.passes, frameGraph, viewerIndex++, framePassCallback); } using ViewerPair = std::pair; @@ -760,14 +772,14 @@ namespace Nz } }); - frameGraph.AddBackbufferOutput(renderTargetData.finalAttachment); + frameGraph.MarkAsFinalOutput(renderTargetData.finalAttachment); } else if (targetViewers.size() == 1) { // Single viewer on that target const auto& viewer = *targetViewers.front(); - frameGraph.AddBackbufferOutput(viewer.finalColorAttachment); + frameGraph.MarkAsFinalOutput(viewer.finalColorAttachment); renderTargetData.finalAttachment = viewer.finalColorAttachment; } } diff --git a/src/Nazara/Graphics/FrameGraph.cpp b/src/Nazara/Graphics/FrameGraph.cpp index 8735dc1e9..4cb13fe14 100644 --- a/src/Nazara/Graphics/FrameGraph.cpp +++ b/src/Nazara/Graphics/FrameGraph.cpp @@ -28,7 +28,7 @@ namespace Nz BakedFrameGraph FrameGraph::Bake() { - if (m_backbufferOutputs.empty()) + if (m_finalOutputs.empty()) throw std::runtime_error("no backbuffer output has been set"); m_pending.attachmentReadList.clear(); @@ -45,7 +45,7 @@ namespace Nz BuildReadWriteList(); - for (std::size_t output : m_backbufferOutputs) + for (std::size_t output : m_finalOutputs) { auto it = m_pending.attachmentWriteList.find(output); if (it == m_pending.attachmentWriteList.end()) @@ -101,10 +101,7 @@ namespace Nz // Add depth-stencil clear values if (const auto& depthStencilClear = framePass.GetDepthStencilClear()) { - std::size_t depthClearIndex = colorOutputs.size(); - bakedPass.outputClearValues.resize(depthClearIndex + 1); - - auto& dsClearValues = bakedPass.outputClearValues[depthClearIndex]; + auto& dsClearValues = bakedPass.outputClearDepthStencil.emplace(); dsClearValues.depth = depthStencilClear->depth; dsClearValues.stencil = depthStencilClear->stencil; } @@ -261,36 +258,42 @@ namespace Nz if (std::holds_alternative(attachmentData)) { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); - - assert(std::find(m_pending.texture2DPool.begin(), m_pending.texture2DPool.end(), textureId) == m_pending.texture2DPool.end()); - m_pending.texture2DPool.push_back(textureId); + if (m_pending.textures[textureId].canReuse) + { + assert(std::find(m_pending.texture2DPool.begin(), m_pending.texture2DPool.end(), textureId) == m_pending.texture2DPool.end()); + m_pending.texture2DPool.push_back(textureId); + } } else if (std::holds_alternative(attachmentData)) { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); - - assert(std::find(m_pending.textureCubePool.begin(), m_pending.textureCubePool.end(), textureId) == m_pending.textureCubePool.end()); - m_pending.texture2DArrayPool.push_back(textureId); + if (m_pending.textures[textureId].canReuse) + { + assert(std::find(m_pending.textureCubePool.begin(), m_pending.textureCubePool.end(), textureId) == m_pending.textureCubePool.end()); + m_pending.texture2DArrayPool.push_back(textureId); + } } else if (std::holds_alternative(attachmentData)) { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); - - assert(std::find(m_pending.textureCubePool.begin(), m_pending.textureCubePool.end(), textureId) == m_pending.textureCubePool.end()); - m_pending.textureCubePool.push_back(textureId); + if (m_pending.textures[textureId].canReuse) + { + assert(std::find(m_pending.textureCubePool.begin(), m_pending.textureCubePool.end(), textureId) == m_pending.textureCubePool.end()); + m_pending.textureCubePool.push_back(textureId); + } } } }); } - // Add TextureUsage::ShaderSampling to backbuffer output - for (std::size_t output : m_backbufferOutputs) + // Add TextureUsage::ShaderSampling and TextureUsage::TransferSource to final outputs + for (std::size_t output : m_finalOutputs) { auto it = m_pending.attachmentToTextures.find(output); assert(it != m_pending.attachmentToTextures.end()); - auto& backbufferTexture = m_pending.textures[it->second]; - backbufferTexture.usage |= TextureUsage::ShaderSampling; + auto& finalTexture = m_pending.textures[it->second]; + finalTexture.usage |= TextureUsage::ShaderSampling | TextureUsage::TransferSource; } // Apply texture view usage to their parents @@ -1015,6 +1018,9 @@ namespace Nz data.size != attachmentData.size) continue; + if (data.size == FramePassAttachmentSize::ViewerTargetFactor && data.viewerIndex != attachmentData.viewerIndex) + continue; + m_pending.texture2DPool.erase(it); m_pending.attachmentToTextures.emplace(attachmentIndex, textureId); @@ -1035,7 +1041,19 @@ namespace Nz data.height = attachmentData.height; data.size = attachmentData.size; data.layerCount = 1; - data.usage = attachmentData.additionalUsage; + data.usage = attachmentData.additionalUsages; + data.viewerIndex = attachmentData.viewerIndex; + data.canReuse = true; + + // Final outputs cannot be reused + for (std::size_t outputAttachmentIndex : m_finalOutputs) + { + if (attachmentIndex == outputAttachmentIndex) + { + data.canReuse = false; + break; + } + } return textureId; } @@ -1058,6 +1076,9 @@ namespace Nz data.layerCount != attachmentData.layerCount) continue; + if (data.size == FramePassAttachmentSize::ViewerTargetFactor && data.viewerIndex != attachmentData.viewerIndex) + continue; + m_pending.texture2DArrayPool.erase(it); m_pending.attachmentToTextures.emplace(attachmentIndex, textureId); @@ -1078,7 +1099,19 @@ namespace Nz data.height = attachmentData.height; data.size = attachmentData.size; data.layerCount = attachmentData.layerCount; - data.usage = attachmentData.additionalUsage; + data.usage = attachmentData.additionalUsages; + data.viewerIndex = attachmentData.viewerIndex; + data.canReuse = true; + + // Final outputs cannot be reused + for (std::size_t outputAttachmentIndex : m_finalOutputs) + { + if (attachmentIndex == outputAttachmentIndex) + { + data.canReuse = false; + break; + } + } return textureId; } @@ -1100,6 +1133,9 @@ namespace Nz data.size != attachmentData.size) continue; + if (data.size == FramePassAttachmentSize::ViewerTargetFactor && data.viewerIndex != attachmentData.viewerIndex) + continue; + m_pending.textureCubePool.erase(it); m_pending.attachmentToTextures.emplace(attachmentIndex, textureId); @@ -1120,7 +1156,19 @@ namespace Nz data.height = attachmentData.height; data.size = attachmentData.size; data.layerCount = 1; - data.usage = attachmentData.additionalUsage; + data.usage = attachmentData.additionalUsages; + data.viewerIndex = attachmentData.viewerIndex; + data.canReuse = true; + + // Final outputs cannot be reused + for (std::size_t outputAttachmentIndex : m_finalOutputs) + { + if (attachmentIndex == outputAttachmentIndex) + { + data.canReuse = false; + break; + } + } return textureId; } @@ -1147,6 +1195,7 @@ namespace Nz parentTextureId, texLayer.layerIndex }; + data.viewerIndex = parentTexture.viewerIndex; return textureId; } diff --git a/src/Nazara/Graphics/PipelinePassList.cpp b/src/Nazara/Graphics/PipelinePassList.cpp index a86e867a8..834ccc570 100644 --- a/src/Nazara/Graphics/PipelinePassList.cpp +++ b/src/Nazara/Graphics/PipelinePassList.cpp @@ -27,7 +27,7 @@ namespace Nz return passes; } - std::size_t PipelinePassList::RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, const FunctionRef& passCallback) const + std::size_t PipelinePassList::RegisterPasses(const std::vector>& passes, FrameGraph& frameGraph, std::optional viewerIndex, const FunctionRef& passCallback) const { NazaraAssert(m_passes.size() == passes.size(), "pass vector size doesn't match passlist size"); @@ -47,7 +47,19 @@ namespace Nz { using T = std::decay_t; if constexpr (std::is_same_v) - return frameGraph.AddAttachment(arg); + { + if (arg.size == FramePassAttachmentSize::ViewerTargetFactor) + { + if (!viewerIndex) + throw std::runtime_error(Format("no viewer index but attachment {} depends on viewer target size", arg.name)); + + FramePassAttachment attachment = arg; + attachment.viewerIndex = *viewerIndex; + return frameGraph.AddAttachment(attachment); + } + else + return frameGraph.AddAttachment(arg); + } else if constexpr (std::is_same_v) return frameGraph.AddAttachmentProxy(arg.name, GetAttachmentIndex(arg.attachmentIndex)); else diff --git a/src/Nazara/Renderer/RenderTexture.cpp b/src/Nazara/Renderer/RenderTexture.cpp new file mode 100644 index 000000000..777535274 --- /dev/null +++ b/src/Nazara/Renderer/RenderTexture.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// 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 +{ + RenderTexture::RenderTexture(std::shared_ptr texture, PipelineStage targetPipelineStage, MemoryAccessFlags targetMemoryFlags, TextureLayout targetLayout) : + m_targetTexture(std::move(texture)), + m_targetMemoryFlags(targetMemoryFlags), + m_targetPipelineStage(targetPipelineStage), + m_targetLayout(targetLayout), + m_textureSize(Vector2ui(m_targetTexture->GetSize())) + { + } + + void RenderTexture::BlitTexture(RenderFrame& /*renderFrame*/, CommandBufferBuilder& builder, const Texture& texture) const + { + Vector3ui textureSize = texture.GetSize(); + Vector3ui targetTextureSize = m_targetTexture->GetSize(); + + builder.BeginDebugRegion("Blit to texture", Color::Blue()); + { + builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::Transfer, MemoryAccess::ColorWrite, MemoryAccess::TransferRead, TextureLayout::ColorOutput, TextureLayout::TransferSource, texture); + builder.TextureBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, {}, MemoryAccess::TransferWrite, TextureLayout::Undefined, TextureLayout::TransferDestination, *m_targetTexture); + + Boxui fromBox(0, 0, 0, textureSize.x, textureSize.y, 1); + Boxui toBox(0, 0, 0, targetTextureSize.x, targetTextureSize.y, 1); + + builder.BlitTexture(texture, fromBox, TextureLayout::TransferSource, *m_targetTexture, toBox, TextureLayout::TransferDestination, SamplerFilter::Linear); + + builder.TextureBarrier(PipelineStage::Transfer, m_targetPipelineStage, MemoryAccess::TransferWrite, m_targetMemoryFlags, TextureLayout::TransferDestination, m_targetLayout, *m_targetTexture); + } + builder.EndDebugRegion(); + } + + const Vector2ui& RenderTexture::GetSize() const + { + return m_textureSize; + } +} diff --git a/src/Nazara/Renderer/WindowSwapchain.cpp b/src/Nazara/Renderer/WindowSwapchain.cpp index f243c3e2e..ae6417167 100644 --- a/src/Nazara/Renderer/WindowSwapchain.cpp +++ b/src/Nazara/Renderer/WindowSwapchain.cpp @@ -30,21 +30,6 @@ namespace Nz ConnectSignals(); } - const Framebuffer& WindowSwapchain::GetFramebuffer(std::size_t i) const - { - return m_swapchain->GetFramebuffer(i); - } - - std::size_t WindowSwapchain::GetFramebufferCount() const - { - return m_swapchain->GetFramebufferCount(); - } - - const RenderPass& WindowSwapchain::GetRenderPass() const - { - return m_swapchain->GetRenderPass(); - } - const Vector2ui& WindowSwapchain::GetSize() const { return (m_swapchain) ? m_swapchain->GetSize() : m_window->GetSize(); diff --git a/tests/ComputeTest/main.cpp b/tests/ComputeTest/main.cpp index b9fe1a91d..fb248cbd1 100644 --- a/tests/ComputeTest/main.cpp +++ b/tests/ComputeTest/main.cpp @@ -167,7 +167,7 @@ int main() } } - const Nz::RenderTarget* windowRT = &windowSwapchain; + const Nz::WindowSwapchain* windowRT = &windowSwapchain; frame.Execute([&](Nz::CommandBufferBuilder& builder) { builder.BeginDebugRegion("Compute part", Nz::Color::Blue()); diff --git a/tests/RenderTest/main.cpp b/tests/RenderTest/main.cpp index 6a9c66d17..74c3f587f 100644 --- a/tests/RenderTest/main.cpp +++ b/tests/RenderTest/main.cpp @@ -355,7 +355,7 @@ int main() debugDrawer.Prepare(frame); - const Nz::RenderTarget& windowRT = windowSwapchain.GetSwapchain(); + const Nz::WindowSwapchain* windowRT = &windowSwapchain; frame.Execute([&](Nz::CommandBufferBuilder& builder) { windowSize = window.GetSize(); @@ -368,7 +368,7 @@ int main() builder.BeginDebugRegion("Main window rendering", Nz::Color::Green()); { - builder.BeginRenderPass(windowRT.GetFramebuffer(frame.GetFramebufferIndex()), windowRT.GetRenderPass(), renderRect, { clearValues[0], clearValues[1] }); + builder.BeginRenderPass(windowRT->GetFramebuffer(frame.GetFramebufferIndex()), windowRT->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] }); { builder.BindIndexBuffer(*renderBufferIB, Nz::IndexType::U16); builder.BindRenderPipeline(*pipeline);