diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 395cd5b2c..fd69c50a3 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -80,12 +81,14 @@ namespace Nz ForwardFramePipeline& operator=(ForwardFramePipeline&&) = delete; private: + struct ViewerData; + BakedFrameGraph BuildFrameGraph(); void RegisterMaterialInstance(MaterialInstance* materialPass); void UnregisterMaterialInstance(MaterialInstance* material); - struct ViewerData; + static std::size_t BuildMergePass(FrameGraph& frameGraph, std::span targetViewers); struct LightData { @@ -119,7 +122,6 @@ namespace Nz struct RenderTargetData { - std::size_t finalAttachment; std::vector viewers; }; diff --git a/include/Nazara/Graphics/FrameGraph.hpp b/include/Nazara/Graphics/FrameGraph.hpp index d69f93c77..a8d915890 100644 --- a/include/Nazara/Graphics/FrameGraph.hpp +++ b/include/Nazara/Graphics/FrameGraph.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,8 @@ namespace Nz { class NAZARA_GRAPHICS_API FrameGraph { + friend class BakedFrameGraph; + public: FrameGraph() = default; FrameGraph(const FrameGraph&) = delete; @@ -38,6 +41,7 @@ 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 std::size_t AddDummyAttachment(); inline FramePass& AddPass(std::string name); inline void AddOutput(std::size_t attachmentIndex); @@ -88,6 +92,10 @@ namespace Nz TextureLayout layout; }; + struct DummyAttachment + { + }; + struct PassBarriers { std::vector invalidationBarriers; @@ -138,7 +146,10 @@ namespace Nz void ReorderPasses(); void TraverseGraph(std::size_t passIndex); - using AttachmentType = std::variant; + using AttachmentType = std::variant; + + static constexpr std::size_t InvalidAttachmentIndex = std::numeric_limits::max(); + static constexpr std::size_t InvalidTextureIndex = std::numeric_limits::max(); std::vector m_graphOutputs; std::vector m_framePasses; diff --git a/include/Nazara/Graphics/FrameGraph.inl b/include/Nazara/Graphics/FrameGraph.inl index d4b70cf9a..eef63f52a 100644 --- a/include/Nazara/Graphics/FrameGraph.inl +++ b/include/Nazara/Graphics/FrameGraph.inl @@ -78,6 +78,14 @@ namespace Nz return id; } + inline std::size_t FrameGraph::AddDummyAttachment() + { + std::size_t id = m_attachments.size(); + m_attachments.emplace_back(DummyAttachment{}); + + return id; + } + inline FramePass& FrameGraph::AddPass(std::string name) { std::size_t id = m_framePasses.size(); diff --git a/include/Nazara/Graphics/FramePass.hpp b/include/Nazara/Graphics/FramePass.hpp index 342cdab3c..cfd5c122e 100644 --- a/include/Nazara/Graphics/FramePass.hpp +++ b/include/Nazara/Graphics/FramePass.hpp @@ -73,7 +73,9 @@ namespace Nz inline void SetDepthStencilInput(std::size_t attachmentId); inline void SetDepthStencilOutput(std::size_t attachmentId); inline void SetExecutionCallback(ExecutionCallback callback); - inline void SetInputLayout(std::size_t inputIndex, TextureLayout layout); + inline void SetInputAccess(std::size_t inputIndex, TextureLayout layout, PipelineStageFlags stageFlags, MemoryAccessFlags accessFlags); + inline void SetInputAssumedLayout(std::size_t inputIndex, TextureLayout layout); + inline void SetInputUsage(std::size_t inputIndex, TextureUsageFlags usageFlags); inline void SetReadInput(std::size_t inputIndex, bool doesRead); FramePass& operator=(const FramePass&) = delete; @@ -90,7 +92,11 @@ namespace Nz struct Input { std::optional assumedLayout; + std::optional textureUsageFlags; std::size_t attachmentId; + MemoryAccessFlags accessFlags = MemoryAccess::ShaderRead; + PipelineStageFlags stageFlags = PipelineStage::FragmentShader; + TextureLayout layout = TextureLayout::ColorInput; bool doesRead = true; }; diff --git a/include/Nazara/Graphics/FramePass.inl b/include/Nazara/Graphics/FramePass.inl index 2137a9a17..79f182d18 100644 --- a/include/Nazara/Graphics/FramePass.inl +++ b/include/Nazara/Graphics/FramePass.inl @@ -127,12 +127,26 @@ namespace Nz m_executionCallback = std::move(callback); } - inline void FramePass::SetInputLayout(std::size_t inputIndex, TextureLayout layout) + inline void FramePass::SetInputAccess(std::size_t inputIndex, TextureLayout layout, PipelineStageFlags stageFlags, MemoryAccessFlags accessFlags) + { + assert(inputIndex < m_inputs.size()); + m_inputs[inputIndex].accessFlags = accessFlags; + m_inputs[inputIndex].layout = layout; + m_inputs[inputIndex].stageFlags = stageFlags; + } + + inline void FramePass::SetInputAssumedLayout(std::size_t inputIndex, TextureLayout layout) { assert(inputIndex < m_inputs.size()); m_inputs[inputIndex].assumedLayout = layout; } + inline void FramePass::SetInputUsage(std::size_t inputIndex, TextureUsageFlags usageFlags) + { + assert(inputIndex < m_inputs.size()); + m_inputs[inputIndex].textureUsageFlags = usageFlags; + } + inline void FramePass::SetReadInput(std::size_t inputIndex, bool doesRead) { assert(inputIndex < m_inputs.size()); diff --git a/include/Nazara/Graphics/RenderTarget.hpp b/include/Nazara/Graphics/RenderTarget.hpp index 936cba9b0..e06be3d79 100644 --- a/include/Nazara/Graphics/RenderTarget.hpp +++ b/include/Nazara/Graphics/RenderTarget.hpp @@ -19,22 +19,33 @@ namespace Nz class Framebuffer; class FrameGraph; class RenderPass; - class Texture; class RenderResources; + class Texture; class NAZARA_GRAPHICS_API RenderTarget { public: - RenderTarget() = default; + inline RenderTarget(Int32 renderOrder = 0); virtual ~RenderTarget(); - virtual void OnBuildGraph(FrameGraph& frameGraph, std::size_t attachmentIndex) const = 0; - virtual void OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const = 0; - + inline Int32 GetRenderOrder() const; virtual const Vector2ui& GetSize() const = 0; + inline bool IsFrameGraphOutput() const; + + virtual std::size_t OnBuildGraph(FrameGraph& frameGraph, std::size_t attachmentIndex) const = 0; + + inline void SetFrameGraphOutput(bool output = true); + + inline void UpdateRenderOrder(Int32 renderOrder); + NazaraSignal(OnRenderTargetRelease, const RenderTarget* /*renderTarget*/); + NazaraSignal(OnRenderTargetRenderOrderChange, const RenderTarget* /*renderTarget*/, Int32 /*newOrder*/); NazaraSignal(OnRenderTargetSizeChange, const RenderTarget* /*renderTarget*/, const Vector2ui& /*newSize*/); + + private: + Int32 m_renderOrder; + bool m_frameGraphOutput; }; } diff --git a/include/Nazara/Graphics/RenderTarget.inl b/include/Nazara/Graphics/RenderTarget.inl index c172f3461..cdd3c2c74 100644 --- a/include/Nazara/Graphics/RenderTarget.inl +++ b/include/Nazara/Graphics/RenderTarget.inl @@ -6,6 +6,32 @@ namespace Nz { + inline RenderTarget::RenderTarget(Int32 renderOrder) : + m_renderOrder(renderOrder), + m_frameGraphOutput(false) + { + } + + inline Int32 RenderTarget::GetRenderOrder() const + { + return m_renderOrder; + } + + inline bool RenderTarget::IsFrameGraphOutput() const + { + return m_frameGraphOutput; + } + + inline void RenderTarget::SetFrameGraphOutput(bool output) + { + m_frameGraphOutput = output; + } + + inline void RenderTarget::UpdateRenderOrder(Int32 renderOrder) + { + OnRenderTargetRenderOrderChange(this, renderOrder); + m_renderOrder = renderOrder; + } } #include diff --git a/include/Nazara/Graphics/RenderTexture.hpp b/include/Nazara/Graphics/RenderTexture.hpp index a9ccb4791..75314eb49 100644 --- a/include/Nazara/Graphics/RenderTexture.hpp +++ b/include/Nazara/Graphics/RenderTexture.hpp @@ -24,8 +24,7 @@ namespace Nz RenderTexture(RenderTexture&&) = delete; ~RenderTexture() = default; - void OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override; - void OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const override; + std::size_t OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override; const Vector2ui& GetSize() const override; diff --git a/include/Nazara/Graphics/RenderTextureBlit.hpp b/include/Nazara/Graphics/RenderTextureBlit.hpp index e1a5a405a..9162297d3 100644 --- a/include/Nazara/Graphics/RenderTextureBlit.hpp +++ b/include/Nazara/Graphics/RenderTextureBlit.hpp @@ -26,8 +26,7 @@ namespace Nz RenderTextureBlit(RenderTextureBlit&&) = delete; ~RenderTextureBlit() = default; - void OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override; - void OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const override; + std::size_t OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override; const Vector2ui& GetSize() const override; diff --git a/include/Nazara/Graphics/RenderWindow.hpp b/include/Nazara/Graphics/RenderWindow.hpp index ab1b3a4d5..85502f0c3 100644 --- a/include/Nazara/Graphics/RenderWindow.hpp +++ b/include/Nazara/Graphics/RenderWindow.hpp @@ -25,14 +25,15 @@ namespace Nz RenderWindow(RenderWindow&&) = delete; ~RenderWindow() = default; - void OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override; - void OnRenderEnd(RenderResources& renderResources, const BakedFrameGraph& frameGraph, std::size_t attachmentId) const override; + std::size_t OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override; const Vector2ui& GetSize() const override; RenderWindow& operator=(const RenderWindow&) = delete; RenderWindow& operator=(RenderWindow&&) = delete; + static constexpr Int32 DefaultRenderOrder = 1000; + private: void SetSwapchain(Swapchain* swapchain); diff --git a/include/Nazara/Graphics/RenderWindow.inl b/include/Nazara/Graphics/RenderWindow.inl index e19a67833..eeaa3e008 100644 --- a/include/Nazara/Graphics/RenderWindow.inl +++ b/include/Nazara/Graphics/RenderWindow.inl @@ -7,6 +7,7 @@ namespace Nz { inline RenderWindow::RenderWindow(Swapchain& swapchain) : + RenderTarget(DefaultRenderOrder), m_swapchain(&swapchain), m_windowSwapchain(nullptr) { diff --git a/src/Nazara/Graphics/BakedFrameGraph.cpp b/src/Nazara/Graphics/BakedFrameGraph.cpp index ee91b91ec..3ae90951e 100644 --- a/src/Nazara/Graphics/BakedFrameGraph.cpp +++ b/src/Nazara/Graphics/BakedFrameGraph.cpp @@ -3,9 +3,9 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include -#include #include namespace Nz @@ -60,7 +60,8 @@ namespace Nz builder.TextureBarrier(textureTransition.srcStageMask, textureTransition.dstStageMask, textureTransition.srcAccessMask, textureTransition.dstAccessMask, textureTransition.oldLayout, textureTransition.newLayout, *texture); } - builder.BeginRenderPass(*passData.framebuffer, *passData.renderPass, passData.renderRect, passData.outputClearValues.data(), passData.outputClearValues.size()); + if (passData.framebuffer) + builder.BeginRenderPass(*passData.framebuffer, *passData.renderPass, passData.renderRect, passData.outputClearValues.data(), passData.outputClearValues.size()); if (!passData.name.empty()) builder.BeginDebugRegion(passData.name, Color::Green()); @@ -85,7 +86,8 @@ namespace Nz if (!passData.name.empty()) builder.EndDebugRegion(); - builder.EndRenderPass(); + if (passData.framebuffer) + builder.EndRenderPass(); }); passData.forceCommandBufferRegeneration = false; @@ -102,7 +104,7 @@ namespace Nz const std::shared_ptr& BakedFrameGraph::GetAttachmentTexture(std::size_t attachmentIndex) const { auto it = m_attachmentToTextureMapping.find(attachmentIndex); - if (it == m_attachmentToTextureMapping.end()) + if (it == m_attachmentToTextureMapping.end() || it->second == FrameGraph::InvalidTextureIndex) { static std::shared_ptr dummy; return dummy; @@ -116,7 +118,7 @@ namespace Nz const std::shared_ptr& BakedFrameGraph::GetRenderPass(std::size_t passIndex) const { auto it = m_attachmentToTextureMapping.find(passIndex); - if (it == m_attachmentToTextureMapping.end()) + if (it == m_attachmentToTextureMapping.end() || it->second == FrameGraph::InvalidTextureIndex) { static std::shared_ptr dummy; return dummy; @@ -224,24 +226,29 @@ namespace Nz { textures.clear(); - unsigned int framebufferWidth = std::numeric_limits::max(); - unsigned int framebufferHeight = std::numeric_limits::max(); - for (std::size_t textureId : passData.outputTextureIndices) + if (!passData.outputTextureIndices.empty()) { - auto& textureData = m_textures[textureId]; - textures.push_back(textureData.texture); + unsigned int framebufferWidth = std::numeric_limits::max(); + unsigned int framebufferHeight = std::numeric_limits::max(); + for (std::size_t textureId : passData.outputTextureIndices) + { + auto& textureData = m_textures[textureId]; + textures.push_back(textureData.texture); - auto [width, height] = ComputeTextureSize(textureData); + auto [width, height] = ComputeTextureSize(textureData); - framebufferWidth = std::min(framebufferWidth, width); - framebufferHeight = std::min(framebufferHeight, height); + framebufferWidth = std::min(framebufferWidth, width); + framebufferHeight = std::min(framebufferHeight, height); + } + + passData.renderRect = Recti(0, 0, int(framebufferWidth), int(framebufferHeight)); + + passData.framebuffer = renderDevice->InstantiateFramebuffer(framebufferWidth, framebufferHeight, passData.renderPass, textures); + if (!passData.name.empty()) + passData.framebuffer->UpdateDebugName(passData.name); } - - passData.renderRect = Recti(0, 0, int(framebufferWidth), int(framebufferHeight)); - - passData.framebuffer = renderDevice->InstantiateFramebuffer(framebufferWidth, framebufferHeight, passData.renderPass, textures); - if (!passData.name.empty()) - passData.framebuffer->UpdateDebugName(passData.name); + else + passData.renderRect = Recti(0, 0, -1, -1); passData.forceCommandBufferRegeneration = true; } diff --git a/src/Nazara/Graphics/DirectionalLightShadowData.cpp b/src/Nazara/Graphics/DirectionalLightShadowData.cpp index bd9c6b272..2933f798e 100644 --- a/src/Nazara/Graphics/DirectionalLightShadowData.cpp +++ b/src/Nazara/Graphics/DirectionalLightShadowData.cpp @@ -217,7 +217,7 @@ namespace Nz PerViewerData& viewerData = *Retrieve(m_viewerData, viewer); std::size_t arrayInputIndex = pass.AddInput(viewerData.textureArrayAttachmentIndex); - pass.SetInputLayout(arrayInputIndex, TextureLayout::ColorInput); + pass.SetInputAssumedLayout(arrayInputIndex, TextureLayout::ColorInput); for (CascadeData& cascade : viewerData.cascades) pass.AddInput(cascade.attachmentIndex); diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index ed0550f80..da37b0560 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -383,21 +383,26 @@ namespace Nz } frameGraphInvalidated |= m_bakedFrameGraph.Resize(renderResources, viewerSizes); + if (frameGraphInvalidated) + { + for (ViewerData& viewerData : m_viewerPool) + { + if (viewerData.blitShaderBinding) + renderResources.PushForRelease(std::move(viewerData.blitShaderBinding)); + } + } // Find active lights (i.e. visible in any frustum) m_activeLights.Clear(); - for (auto& viewerData : m_viewerPool) + for (ViewerData* viewerData : m_orderedViewers) { - if (viewerData.pendingDestruction) - continue; - - UInt32 renderMask = viewerData.viewer->GetRenderMask(); + UInt32 renderMask = viewerData->viewer->GetRenderMask(); // Extract frustum from viewproj matrix - const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix(); - viewerData.frame.frustum = Frustumf::Extract(viewProjMatrix); + const Matrix4f& viewProjMatrix = viewerData->viewer->GetViewerInstance().GetViewProjMatrix(); + viewerData->frame.frustum = Frustumf::Extract(viewProjMatrix); - viewerData.frame.visibleLights.Clear(); + viewerData->frame.visibleLights.Clear(); for (auto it = m_lightPool.begin(); it != m_lightPool.end(); ++it) { const LightData& lightData = *it; @@ -407,7 +412,7 @@ namespace Nz continue; m_activeLights.UnboundedSet(lightIndex); - viewerData.frame.visibleLights.UnboundedSet(lightIndex); + viewerData->frame.visibleLights.UnboundedSet(lightIndex); } } @@ -422,61 +427,34 @@ namespace Nz } // Viewer handling (second pass) - for (auto& viewerData : m_viewerPool) + for (ViewerData* viewerData : m_orderedViewers) { - if (viewerData.pendingDestruction) - continue; - - UInt32 renderMask = viewerData.viewer->GetRenderMask(); + UInt32 renderMask = viewerData->viewer->GetRenderMask(); // Per-viewer shadow map handling - for (std::size_t lightIndex : viewerData.frame.visibleLights.IterBits()) + for (std::size_t lightIndex : viewerData->frame.visibleLights.IterBits()) { LightData* lightData = m_lightPool.RetrieveFromIndex(lightIndex); if (lightData->shadowData && lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0) - lightData->shadowData->PrepareRendering(renderResources, viewerData.viewer); + lightData->shadowData->PrepareRendering(renderResources, viewerData->viewer); } // Frustum culling std::size_t visibilityHash = 5; - const auto& visibleRenderables = FrustumCull(viewerData.frame.frustum, renderMask, visibilityHash); + const auto& visibleRenderables = FrustumCull(viewerData->frame.frustum, renderMask, visibilityHash); FramePipelinePass::FrameData passData = { - &viewerData.frame.visibleLights, - viewerData.frame.frustum, + &viewerData->frame.visibleLights, + viewerData->frame.frustum, renderResources, visibleRenderables, visibilityHash }; - for (auto& passPtr : viewerData.passes) + for (auto& passPtr : viewerData->passes) passPtr->Prepare(passData); } - if (frameGraphInvalidated) - { - const std::shared_ptr& sampler = graphics->GetSamplerCache().Get({}); - for (auto& viewerData : m_viewerPool) - { - if (viewerData.pendingDestruction) - continue; - - if (viewerData.blitShaderBinding) - renderResources.PushForRelease(std::move(viewerData.blitShaderBinding)); - - viewerData.blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0); - viewerData.blitShaderBinding->Update({ - { - 0, - ShaderBinding::SampledTextureBinding { - m_bakedFrameGraph.GetAttachmentTexture(viewerData.finalColorAttachment).get(), - sampler.get() - } - } - }); - } - } - // Update UBOs and materials renderResources.Execute([&](CommandBufferBuilder& builder) { @@ -498,10 +476,6 @@ namespace Nz m_bakedFrameGraph.Execute(renderResources); m_rebuildFrameGraph = false; - // Final blit (TODO: Make part of frame graph?) - for (auto&& [renderTargetPtr, renderTargetData] : m_renderTargets) - renderTargetPtr->OnRenderEnd(renderResources, m_bakedFrameGraph, renderTargetData.finalAttachment); - // reset at the end instead of the beginning so debug draw can be used before calling this method DebugDrawer& debugDrawer = GetDebugDrawer(); debugDrawer.Reset(renderResources); @@ -648,6 +622,7 @@ namespace Nz { FrameGraph frameGraph; + // Register viewer-independent passes for (std::size_t i : m_shadowCastingLights.IterBits()) { LightData* lightData = m_lightPool.RetrieveFromIndex(i); @@ -655,149 +630,177 @@ namespace Nz lightData->shadowData->RegisterToFrameGraph(frameGraph, nullptr); } - using ViewerPair = std::pair; - - StackArray viewers = NazaraStackArray(ViewerPair, m_viewerPool.size()); - auto viewerIt = viewers.begin(); - + // Group every viewer by their render order and RenderTarget + m_orderedViewers.clear(); for (auto& viewerData : m_viewerPool) { if (viewerData.pendingDestruction) continue; - const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget(); - *viewerIt++ = std::make_pair(&renderTarget, &viewerData); + m_orderedViewers.push_back(&viewerData); } - std::sort(viewers.begin(), viewers.end(), [](const ViewerPair& lhs, const ViewerPair& rhs) + if (m_orderedViewers.empty()) + return frameGraph.Bake(); + + std::sort(m_orderedViewers.begin(), m_orderedViewers.end(), [](ViewerData* lhs, ViewerData* rhs) { - return lhs.second->renderOrder < rhs.second->renderOrder; + // Order by RenderTarget render order and then by viewer render order + Int32 leftTargetRenderOrder1 = lhs->viewer->GetRenderTarget().GetRenderOrder(); + Int32 rightTargetRenderOrder1 = rhs->viewer->GetRenderTarget().GetRenderOrder(); + + if (leftTargetRenderOrder1 == rightTargetRenderOrder1) + return leftTargetRenderOrder1 < rightTargetRenderOrder1; + else + return lhs->renderOrder < rhs->renderOrder; }); - StackVector dependenciesColorAttachments = NazaraStackVector(std::size_t, viewers.size()); + StackVector dependenciesColorAttachments = NazaraStackVector(std::size_t, m_orderedViewers.size()); + std::size_t dependenciesColorAttachmentCount = 0; + Int32 lastRenderOrder = m_orderedViewers.front()->viewer->GetRenderTarget().GetRenderOrder(); - m_orderedViewers.clear(); - m_renderTargets.clear(); - unsigned int viewerIndex = 0; - for (auto it = viewers.begin(), prevIt = it; it != viewers.end(); ++it) + std::size_t viewerIndex = 0; + auto HandleRenderTarget = [&](const RenderTarget& renderTarget, std::span viewers) { - auto&& [renderTarget, viewerData] = *it; - - UInt32 renderMask = viewerData->viewer->GetRenderMask(); - for (std::size_t i : m_shadowCastingLights.IterBits()) + if (renderTarget.GetRenderOrder() > lastRenderOrder) { - LightData* lightData = m_lightPool.RetrieveFromIndex(i); - if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0) - lightData->shadowData->RegisterToFrameGraph(frameGraph, viewerData->viewer); + dependenciesColorAttachmentCount = dependenciesColorAttachments.size(); + lastRenderOrder = renderTarget.GetRenderOrder(); } - // Keep track of previous dependencies attachments (from viewers having a smaller render order) - Int32 renderOrder = viewerData->renderOrder; - for (auto it2 = prevIt; prevIt != it; ++prevIt) - { - ViewerData* prevViewerData = prevIt->second; - Int32 prevRenderOrder = prevViewerData->renderOrder; - if (prevRenderOrder >= renderOrder) - break; + assert(!viewers.empty()); - dependenciesColorAttachments.push_back(prevViewerData->finalColorAttachment); - prevIt = it2; - } - - auto framePassCallback = [&, viewerData = viewerData](std::size_t /*passIndex*/, FramePass& framePass, FramePipelinePassFlags flags) + for (ViewerData* viewerData : viewers) { - // Inject previous final attachments as inputs for all passes, to force framegraph to order viewers passes relative to each other - // TODO: Allow the user to define which pass of viewer A uses viewer B rendering - for (std::size_t finalAttachment : dependenciesColorAttachments) + UInt32 renderMask = viewerData->viewer->GetRenderMask(); + for (std::size_t i : m_shadowCastingLights.IterBits()) { - std::size_t inputIndex = framePass.AddInput(finalAttachment); - - // Disable ReadInput to prevent the framegraph from transitionning the texture layout (for now it's handled externally) - // (however if we manage to get rid of the texture blit from RenderTexture by making the framegraph use the external texture directly, this would be necessary) - framePass.SetReadInput(inputIndex, false); + LightData* lightData = m_lightPool.RetrieveFromIndex(i); + if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0) + lightData->shadowData->RegisterToFrameGraph(frameGraph, viewerData->viewer); } - if (flags.Test(FramePipelinePassFlag::LightShadowing)) + auto framePassCallback = [&, viewerData = viewerData](std::size_t /*passIndex*/, FramePass& framePass, FramePipelinePassFlags flags) { - for (std::size_t i : m_shadowCastingLights.IterBits()) + // Inject previous final attachments as inputs for all passes, to force framegraph to order viewers passes relative to each other + // TODO: Allow the user to define which pass of viewer A uses viewer B rendering + for (std::size_t i = 0; i < dependenciesColorAttachmentCount; ++i) + framePass.AddInput(dependenciesColorAttachments[i]); + + if (flags.Test(FramePipelinePassFlag::LightShadowing)) { - LightData* lightData = m_lightPool.RetrieveFromIndex(i); - if ((renderMask & lightData->renderMask) != 0) - lightData->shadowData->RegisterPassInputs(framePass, (lightData->shadowData->IsPerViewer()) ? viewerData->viewer : nullptr); - } - } - }; - - viewerData->finalColorAttachment = viewerData->viewer->RegisterPasses(viewerData->passes, frameGraph, viewerIndex++, framePassCallback); - - // Group viewers by render targets - auto& renderTargetData = m_renderTargets[renderTarget]; - renderTargetData.viewers.push_back(viewerData); - m_orderedViewers.push_back(viewerData); - } - - for (auto&& [renderTarget, renderTargetData] : m_renderTargets) - { - const auto& targetViewers = renderTargetData.viewers; - - if (targetViewers.size() > 1) - { - // Multiple viewers on the same targets, merge them - FramePass& mergePass = frameGraph.AddPass("Merge pass"); - - renderTargetData.finalAttachment = frameGraph.AddAttachment({ - "Viewer output", - PixelFormat::RGBA8 - }); - - for (const ViewerData* viewerData : targetViewers) - mergePass.AddInput(viewerData->finalColorAttachment); - - mergePass.AddOutput(renderTargetData.finalAttachment); - mergePass.SetClearColor(0, Color::Black()); - - mergePass.SetCommandCallback([&targetViewers](CommandBufferBuilder& builder, const FramePassEnvironment& /*env*/) - { - Graphics* graphics = Graphics::Instance(); - builder.BindRenderPipeline(*graphics->GetBlitPipeline(false)); - - bool first = true; - - for (const ViewerData* viewerData : targetViewers) - { - Recti renderRect = viewerData->viewer->GetViewport(); - - builder.SetScissor(renderRect); - builder.SetViewport(renderRect); - - const ShaderBindingPtr& blitShaderBinding = viewerData->blitShaderBinding; - - builder.BindRenderShaderBinding(0, *blitShaderBinding); - builder.Draw(3); - - if (first) + for (std::size_t i : m_shadowCastingLights.IterBits()) { - builder.BindRenderPipeline(*graphics->GetBlitPipeline(true)); - first = false; + LightData* lightData = m_lightPool.RetrieveFromIndex(i); + if ((renderMask & lightData->renderMask) != 0) + lightData->shadowData->RegisterPassInputs(framePass, (lightData->shadowData->IsPerViewer()) ? viewerData->viewer : nullptr); } } - }); + }; - renderTarget->OnBuildGraph(frameGraph, renderTargetData.finalAttachment); + viewerData->finalColorAttachment = viewerData->viewer->RegisterPasses(viewerData->passes, frameGraph, viewerIndex++, framePassCallback); } - else if (targetViewers.size() == 1) + + std::size_t finalAttachment; + if (viewers.size() > 1) + { + // Multiple viewers on the same targets, merge them + finalAttachment = renderTarget.OnBuildGraph(frameGraph, BuildMergePass(frameGraph, viewers)); + } + else { // Single viewer on that target - const auto& viewer = *targetViewers.front(); + const auto& viewer = *viewers.front(); + finalAttachment = renderTarget.OnBuildGraph(frameGraph, viewer.finalColorAttachment); + } - renderTargetData.finalAttachment = viewer.finalColorAttachment; - renderTarget->OnBuildGraph(frameGraph, renderTargetData.finalAttachment); + if (!renderTarget.IsFrameGraphOutput()) + { + // Keep track of previous dependencies attachments + dependenciesColorAttachments.push_back(finalAttachment); + } + else + frameGraph.AddOutput(finalAttachment); + }; + + const RenderTarget* currentTarget = &m_orderedViewers.front()->viewer->GetRenderTarget(); + std::size_t currentTargetIndex = 0; + for (std::size_t i = 1; i < m_orderedViewers.size(); ++i) + { + const RenderTarget* target = &m_orderedViewers[i]->viewer->GetRenderTarget(); + if (currentTarget != target) + { + HandleRenderTarget(*currentTarget, std::span(&m_orderedViewers[currentTargetIndex], &m_orderedViewers[i])); + currentTarget = target; + currentTargetIndex = i; } } + HandleRenderTarget(*currentTarget, std::span(m_orderedViewers.data() + currentTargetIndex, m_orderedViewers.data() + m_orderedViewers.size())); return frameGraph.Bake(); } + std::size_t ForwardFramePipeline::BuildMergePass(FrameGraph& frameGraph, std::span targetViewers) + { + FramePass& mergePass = frameGraph.AddPass("Merge pass"); + + std::size_t mergedAttachment = frameGraph.AddAttachment({ + "Merged output", + PixelFormat::RGBA8 + }); + + for (const ViewerData* viewerData : targetViewers) + mergePass.AddInput(viewerData->finalColorAttachment); + + mergePass.AddOutput(mergedAttachment); + mergePass.SetClearColor(0, Color::Black()); + + mergePass.SetCommandCallback([&targetViewers](CommandBufferBuilder& builder, const FramePassEnvironment& env) + { + Graphics* graphics = Graphics::Instance(); + builder.BindRenderPipeline(*graphics->GetBlitPipeline(false)); + + bool first = true; + + for (ViewerData* viewerData : targetViewers) + { + Recti renderRect = viewerData->viewer->GetViewport(); + + builder.SetScissor(renderRect); + builder.SetViewport(renderRect); + + if (!viewerData->blitShaderBinding) + { + const std::shared_ptr& sampler = graphics->GetSamplerCache().Get({}); + + viewerData->blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0); + viewerData->blitShaderBinding->Update({ + { + 0, + ShaderBinding::SampledTextureBinding { + env.frameGraph.GetAttachmentTexture(viewerData->finalColorAttachment).get(), + sampler.get() + } + } + }); + } + + const ShaderBindingPtr& blitShaderBinding = viewerData->blitShaderBinding; + + builder.BindRenderShaderBinding(0, *blitShaderBinding); + builder.Draw(3); + + if (first) + { + builder.BindRenderPipeline(*graphics->GetBlitPipeline(true)); + first = false; + } + } + }); + + return mergedAttachment; + } + void ForwardFramePipeline::RegisterMaterialInstance(MaterialInstance* materialInstance) { auto it = m_materialInstances.find(materialInstance); diff --git a/src/Nazara/Graphics/FrameGraph.cpp b/src/Nazara/Graphics/FrameGraph.cpp index 8099cb4fd..b194c2ba3 100644 --- a/src/Nazara/Graphics/FrameGraph.cpp +++ b/src/Nazara/Graphics/FrameGraph.cpp @@ -29,7 +29,7 @@ namespace Nz BakedFrameGraph FrameGraph::Bake() { if (m_graphOutputs.empty()) - throw std::runtime_error("no backbuffer output has been set"); + throw std::runtime_error("no graph output has been set"); m_pending.attachmentReadList.clear(); m_pending.attachmentToTextures.clear(); @@ -89,7 +89,8 @@ namespace Nz for (std::size_t i = 0; i < colorOutputs.size(); ++i) { const auto& output = colorOutputs[i]; - bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, output.attachmentId)); + if (std::size_t textureIndex = Retrieve(m_pending.attachmentToTextures, output.attachmentId); textureIndex != InvalidTextureIndex) + bakedPass.outputTextureIndices.push_back(textureIndex); if (output.clearColor) { @@ -111,9 +112,15 @@ namespace Nz std::size_t attachmentId; if (attachmentId = framePass.GetDepthStencilOutput(); attachmentId != FramePass::InvalidAttachmentId) - bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, attachmentId)); + { + if (std::size_t textureIndex = Retrieve(m_pending.attachmentToTextures, attachmentId); textureIndex != InvalidTextureIndex) + bakedPass.outputTextureIndices.push_back(textureIndex); + } else if (attachmentId = framePass.GetDepthStencilInput(); attachmentId != FramePass::InvalidAttachmentId) - bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, attachmentId)); //< FIXME? + { + if (std::size_t textureIndex = Retrieve(m_pending.attachmentToTextures, attachmentId); textureIndex != InvalidTextureIndex) + bakedPass.outputTextureIndices.push_back(textureIndex); + } } } @@ -199,25 +206,31 @@ namespace Nz for (const auto& input : framePass.GetInputs()) { std::size_t textureId = RegisterTexture(input.attachmentId); - - FrameGraphTextureData& attachmentData = m_pending.textures[textureId]; - attachmentData.usage |= TextureUsage::ShaderSampling; + if (textureId != InvalidTextureIndex) + { + FrameGraphTextureData& attachmentData = m_pending.textures[textureId]; + attachmentData.usage |= input.textureUsageFlags.value_or(TextureUsage::ShaderSampling); + } } for (const auto& output : framePass.GetOutputs()) { std::size_t textureId = RegisterTexture(output.attachmentId); - - FrameGraphTextureData& attachmentData = m_pending.textures[textureId]; - attachmentData.usage |= TextureUsage::ColorAttachment; + if (textureId != InvalidTextureIndex) + { + FrameGraphTextureData& attachmentData = m_pending.textures[textureId]; + attachmentData.usage |= TextureUsage::ColorAttachment; + } } if (std::size_t depthStencilInput = framePass.GetDepthStencilInput(); depthStencilInput != FramePass::InvalidAttachmentId) { std::size_t textureId = RegisterTexture(depthStencilInput); - - FrameGraphTextureData& attachmentData = m_pending.textures[textureId]; - attachmentData.usage |= TextureUsage::DepthStencilAttachment; + if (textureId != InvalidTextureIndex) + { + FrameGraphTextureData& attachmentData = m_pending.textures[textureId]; + attachmentData.usage |= TextureUsage::DepthStencilAttachment; + } if (std::size_t depthStencilOutput = framePass.GetDepthStencilOutput(); depthStencilOutput != FramePass::InvalidAttachmentId) { @@ -296,8 +309,11 @@ namespace Nz auto it = m_pending.attachmentToTextures.find(output); assert(it != m_pending.attachmentToTextures.end()); - auto& finalTexture = m_pending.textures[it->second]; - finalTexture.usage |= TextureUsage::ShaderSampling | TextureUsage::TransferSource; + if (std::size_t textureIndex = it->second; textureIndex != InvalidTextureIndex) + { + auto& finalTexture = m_pending.textures[textureIndex]; + finalTexture.usage |= TextureUsage::ShaderSampling | TextureUsage::TransferSource; + } } // Apply texture view usage to their parents @@ -316,13 +332,15 @@ namespace Nz assert(m_pending.barrierList.empty()); m_pending.barrierList.reserve(m_pending.passList.size()); - auto GetBarrier = [&](std::vector& barriers, std::size_t attachmentId) -> Barrier& + auto GetBarrier = [&](std::vector& barriers, std::size_t attachmentId) -> Barrier* { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, ResolveAttachmentIndex(attachmentId)); + if (textureId == InvalidTextureIndex) + return nullptr; auto it = std::find_if(barriers.begin(), barriers.end(), [&](const Barrier& barrier) { return barrier.textureId == textureId; }); if (it != barriers.end()) - return *it; + return &*it; else { // Insert a new barrier @@ -330,7 +348,7 @@ namespace Nz barrier.textureId = textureId; barrier.layout = TextureLayout::Undefined; - return barrier; + return &barrier; } }; @@ -340,29 +358,35 @@ namespace Nz auto& barriers = m_pending.barrierList.emplace_back(); - auto GetInvalidationBarrier = [&](std::size_t attachmentId) -> Barrier& { return GetBarrier(barriers.invalidationBarriers, attachmentId); }; - auto GetFlushBarrier = [&](std::size_t attachmentId) -> Barrier& { return GetBarrier(barriers.flushBarriers, attachmentId); }; + auto GetInvalidationBarrier = [&](std::size_t attachmentId) -> Barrier* { return GetBarrier(barriers.invalidationBarriers, attachmentId); }; + auto GetFlushBarrier = [&](std::size_t attachmentId) -> Barrier* { return GetBarrier(barriers.flushBarriers, attachmentId); }; for (const auto& input : framePass.GetInputs()) { - auto& barrier = GetInvalidationBarrier(input.attachmentId); - if (barrier.layout != TextureLayout::Undefined) + Barrier* barrier = GetInvalidationBarrier(input.attachmentId); + if (!barrier) + continue; + + if (barrier->layout != TextureLayout::Undefined) throw std::runtime_error("layout mismatch"); - barrier.access |= MemoryAccess::ShaderRead; - barrier.stages |= PipelineStage::FragmentShader; - barrier.layout = TextureLayout::ColorInput; + barrier->access |= input.accessFlags; + barrier->stages |= input.stageFlags; + barrier->layout = input.layout; } for (const auto& output : framePass.GetOutputs()) { - auto& barrier = GetFlushBarrier(output.attachmentId); - if (barrier.layout != TextureLayout::Undefined) + Barrier* barrier = GetFlushBarrier(output.attachmentId); + if (!barrier) + continue; + + if (barrier->layout != TextureLayout::Undefined) throw std::runtime_error("layout mismatch"); - barrier.access |= MemoryAccess::ColorWrite; - barrier.stages |= PipelineStage::ColorOutput; - barrier.layout = TextureLayout::ColorOutput; + barrier->access |= MemoryAccess::ColorWrite; + barrier->stages |= PipelineStage::ColorOutput; + barrier->layout = TextureLayout::ColorOutput; } std::size_t dsInputAttachment = framePass.GetDepthStencilInput(); @@ -371,40 +395,48 @@ namespace Nz if (dsInputAttachment != FramePass::InvalidAttachmentId && dsOutputAttachement != FramePass::InvalidAttachmentId) { // DS input/output - auto& invalidationBarrier = GetInvalidationBarrier(dsInputAttachment); - if (invalidationBarrier.layout != TextureLayout::Undefined) - throw std::runtime_error("layout mismatch"); + if (Barrier* invalidationBarrier = GetInvalidationBarrier(dsInputAttachment)) + { + if (invalidationBarrier->layout != TextureLayout::Undefined) + throw std::runtime_error("layout mismatch"); - invalidationBarrier.layout = TextureLayout::DepthStencilReadWrite; - invalidationBarrier.access = MemoryAccess::DepthStencilRead | MemoryAccess::DepthStencilWrite; - invalidationBarrier.stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate; + invalidationBarrier->layout = TextureLayout::DepthStencilReadWrite; + invalidationBarrier->access = MemoryAccess::DepthStencilRead | MemoryAccess::DepthStencilWrite; + invalidationBarrier->stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate; + } - auto& flushBarrier = GetFlushBarrier(dsOutputAttachement); - flushBarrier.layout = TextureLayout::DepthStencilReadWrite; - flushBarrier.access = MemoryAccess::DepthStencilWrite; - flushBarrier.stages = PipelineStage::FragmentTestsLate; + if (Barrier* flushBarrier = GetFlushBarrier(dsOutputAttachement)) + { + flushBarrier->layout = TextureLayout::DepthStencilReadWrite; + flushBarrier->access = MemoryAccess::DepthStencilWrite; + flushBarrier->stages = PipelineStage::FragmentTestsLate; + } } else if (dsInputAttachment != FramePass::InvalidAttachmentId) { // DS input-only - auto& invalidationBarrier = GetInvalidationBarrier(dsInputAttachment); - if (invalidationBarrier.layout != TextureLayout::Undefined) - throw std::runtime_error("layout mismatch"); + if (Barrier* invalidationBarrier = GetInvalidationBarrier(dsInputAttachment)) + { + if (invalidationBarrier->layout != TextureLayout::Undefined) + throw std::runtime_error("layout mismatch"); - invalidationBarrier.layout = TextureLayout::DepthStencilReadWrite; - invalidationBarrier.access = MemoryAccess::DepthStencilRead; - invalidationBarrier.stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate; + invalidationBarrier->layout = TextureLayout::DepthStencilReadWrite; + invalidationBarrier->access = MemoryAccess::DepthStencilRead; + invalidationBarrier->stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate; + } } else if (dsOutputAttachement != FramePass::InvalidAttachmentId) { // DS output-only - auto& flushBarrier = GetFlushBarrier(dsOutputAttachement); - if (flushBarrier.layout != TextureLayout::Undefined) - throw std::runtime_error("layout mismatch"); + if (Barrier* flushBarrier = GetFlushBarrier(dsOutputAttachement)) + { + if (flushBarrier->layout != TextureLayout::Undefined) + throw std::runtime_error("layout mismatch"); - flushBarrier.layout = TextureLayout::DepthStencilReadWrite; - flushBarrier.access = MemoryAccess::DepthStencilWrite; - flushBarrier.stages = PipelineStage::FragmentTestsLate; + flushBarrier->layout = TextureLayout::DepthStencilReadWrite; + flushBarrier->access = MemoryAccess::DepthStencilWrite; + flushBarrier->stages = PipelineStage::FragmentTestsLate; + } } } } @@ -729,12 +761,14 @@ namespace Nz auto RegisterColorInputRead = [&](const FramePass::Input& input) { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId); + if (textureId == InvalidTextureIndex) + return; TextureLayout& textureLayout = textureLayouts[textureId]; if (!input.assumedLayout) { assert(textureLayouts[textureId] != TextureLayout::Undefined); - textureLayout = TextureLayout::ColorInput; + textureLayout = input.layout; } else textureLayout = *input.assumedLayout; @@ -743,6 +777,8 @@ namespace Nz auto RegisterColorOutput = [&](const FramePass::Output& output, bool shouldLoad) { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, output.attachmentId); + if (textureId == InvalidTextureIndex) + return InvalidAttachmentIndex; TextureLayout initialLayout = textureLayouts[textureId]; textureLayouts[textureId] = TextureLayout::ColorOutput; @@ -770,19 +806,21 @@ namespace Nz return attachmentIndex; }; - auto RegisterDepthStencil = [&](std::size_t attachmentId, TextureLayout textureLayout, bool* first) -> RenderPass::Attachment& + auto RegisterDepthStencil = [&](std::size_t attachmentId, TextureLayout textureLayout, bool* first) -> RenderPass::Attachment* { if (depthStencilAttachmentIndex) { assert(depthStencilAttachmentId == attachmentId); *first = false; - return renderPassAttachments[depthStencilAttachmentIndex.value()]; + return &renderPassAttachments[depthStencilAttachmentIndex.value()]; } *first = true; std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); + if (textureId == InvalidTextureIndex) + return nullptr; TextureLayout initialLayout = textureLayouts[textureId]; textureLayouts[textureId] = textureLayout; @@ -796,7 +834,7 @@ namespace Nz depthStencilAttachment.format = m_pending.textures[textureId].format; depthStencilAttachment.initialLayout = initialLayout; - return depthStencilAttachment; + return &depthStencilAttachment; }; std::size_t physicalPassIndex = 0; @@ -834,11 +872,13 @@ namespace Nz shouldLoad = true; std::size_t attachmentIndex = RegisterColorOutput(output, shouldLoad); - - colorAttachments.push_back({ - attachmentIndex, - TextureLayout::ColorOutput - }); + if (attachmentIndex != InvalidAttachmentIndex) + { + colorAttachments.push_back({ + attachmentIndex, + TextureLayout::ColorOutput + }); + } } } @@ -855,73 +895,79 @@ namespace Nz { // DS input/output bool first; - auto& dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadWrite, &first); - - if (first) + RenderPass::Attachment* dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadWrite, &first); + if (dsAttachment) { - dsAttachment.loadOp = AttachmentLoadOp::Load; - dsAttachment.storeOp = AttachmentStoreOp::Store; - } + if (first) + { + dsAttachment->loadOp = AttachmentLoadOp::Load; + dsAttachment->storeOp = AttachmentStoreOp::Store; + } - depthStencilAttachment = RenderPass::AttachmentReference{ - depthStencilAttachmentIndex.value(), - TextureLayout::DepthStencilReadWrite - }; + depthStencilAttachment = RenderPass::AttachmentReference{ + depthStencilAttachmentIndex.value(), + TextureLayout::DepthStencilReadWrite + }; + } } else if (dsInputAttachment != FramePass::InvalidAttachmentId) { // DS input-only bool first; - auto& dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadOnly, &first); - - if (first) + RenderPass::Attachment* dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadOnly, &first); + if (dsAttachment) { - bool canDiscard = true; - - // Check if a future pass reads from the DS buffer or if we can discard it after this pass - if (auto readIt = m_pending.attachmentReadList.find(dsInputAttachment); readIt != m_pending.attachmentReadList.end()) + if (first) { - for (std::size_t passIndex : readIt->second) - { - auto it = m_pending.passIdToPhysicalPassIndex.find(passIndex); - if (it == m_pending.passIdToPhysicalPassIndex.end()) - continue; //< pass may have been discarded + bool canDiscard = true; - std::size_t readPhysicalPassIndex = it->second; - if (readPhysicalPassIndex > physicalPassIndex) //< Read in a future pass? + // Check if a future pass reads from the DS buffer or if we can discard it after this pass + if (auto readIt = m_pending.attachmentReadList.find(dsInputAttachment); readIt != m_pending.attachmentReadList.end()) + { + for (std::size_t passIndex : readIt->second) { - // Yes, store it - canDiscard = false; - break; + auto it = m_pending.passIdToPhysicalPassIndex.find(passIndex); + if (it == m_pending.passIdToPhysicalPassIndex.end()) + continue; //< pass may have been discarded + + std::size_t readPhysicalPassIndex = it->second; + if (readPhysicalPassIndex > physicalPassIndex) //< Read in a future pass? + { + // Yes, store it + canDiscard = false; + break; + } } } + + dsAttachment->storeOp = (canDiscard) ? AttachmentStoreOp::Discard : AttachmentStoreOp::Store; } - dsAttachment.storeOp = (canDiscard) ? AttachmentStoreOp::Discard : AttachmentStoreOp::Store; + depthStencilAttachment = RenderPass::AttachmentReference{ + depthStencilAttachmentIndex.value(), + TextureLayout::DepthStencilReadOnly + }; } - - depthStencilAttachment = RenderPass::AttachmentReference{ - depthStencilAttachmentIndex.value(), - TextureLayout::DepthStencilReadOnly - }; } else if (dsOutputAttachement != FramePass::InvalidAttachmentId) { // DS output-only bool first; - auto& dsAttachment = RegisterDepthStencil(dsOutputAttachement, TextureLayout::DepthStencilReadWrite, &first); - - if (first) + RenderPass::Attachment* dsAttachment = RegisterDepthStencil(dsOutputAttachement, TextureLayout::DepthStencilReadWrite, &first); + if (dsAttachment) { - dsAttachment.initialLayout = TextureLayout::Undefined; //< Don't care about initial layout - dsAttachment.loadOp = (framePass.GetDepthStencilClear()) ? AttachmentLoadOp::Clear : AttachmentLoadOp::Discard; - dsAttachment.storeOp = AttachmentStoreOp::Store; - } + if (first) + { + dsAttachment->initialLayout = TextureLayout::Undefined; //< Don't care about initial layout + dsAttachment->loadOp = (framePass.GetDepthStencilClear()) ? AttachmentLoadOp::Clear : AttachmentLoadOp::Discard; + dsAttachment->storeOp = AttachmentStoreOp::Store; + } - depthStencilAttachment = RenderPass::AttachmentReference{ - depthStencilAttachmentIndex.value(), - TextureLayout::DepthStencilReadWrite - }; + depthStencilAttachment = RenderPass::AttachmentReference{ + depthStencilAttachmentIndex.value(), + TextureLayout::DepthStencilReadWrite + }; + } } subpassesDesc.push_back({ @@ -943,7 +989,10 @@ namespace Nz BuildPhysicalPassDependencies(colorAttachmentCount, depthStencilAttachmentIndex.has_value(), renderPassAttachments, subpassesDesc, subpassesDeps); - m_pending.renderPasses.push_back(renderPassCache.Get(renderPassAttachments, subpassesDesc, subpassesDeps)); + if (!renderPassAttachments.empty()) + m_pending.renderPasses.push_back(renderPassCache.Get(renderPassAttachments, subpassesDesc, subpassesDeps)); + else + m_pending.renderPasses.push_back(nullptr); physicalPassIndex++; } @@ -1223,6 +1272,11 @@ namespace Nz return textureId; } + else if constexpr (std::is_same_v) + { + m_pending.attachmentToTextures.emplace(attachmentIndex, InvalidTextureIndex); + return InvalidTextureIndex; + } else static_assert(AlwaysFalse::value, "non-exhaustive visitor"); }, m_attachments[attachmentIndex]); diff --git a/src/Nazara/Graphics/PointLightShadowData.cpp b/src/Nazara/Graphics/PointLightShadowData.cpp index 3c53252de..97c4369c5 100644 --- a/src/Nazara/Graphics/PointLightShadowData.cpp +++ b/src/Nazara/Graphics/PointLightShadowData.cpp @@ -135,7 +135,7 @@ namespace Nz assert(viewer == nullptr); std::size_t cubeInputIndex = pass.AddInput(m_cubeAttachmentIndex); - pass.SetInputLayout(cubeInputIndex, TextureLayout::ColorInput); + pass.SetInputAssumedLayout(cubeInputIndex, TextureLayout::ColorInput); for (DirectionData& direction : m_directions) pass.AddInput(direction.attachmentIndex); diff --git a/src/Nazara/Graphics/RenderTexture.cpp b/src/Nazara/Graphics/RenderTexture.cpp index 3f6165909..0914e13f2 100644 --- a/src/Nazara/Graphics/RenderTexture.cpp +++ b/src/Nazara/Graphics/RenderTexture.cpp @@ -15,13 +15,10 @@ namespace Nz { } - void RenderTexture::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const + std::size_t RenderTexture::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const { graph.BindExternalTexture(attachmentIndex, m_targetTexture); - } - - void RenderTexture::OnRenderEnd(RenderResources& /*renderFrame*/, const BakedFrameGraph& /*frameGraph*/, std::size_t /*finalAttachment*/) const - { + return attachmentIndex; } const Vector2ui& RenderTexture::GetSize() const diff --git a/src/Nazara/Graphics/RenderTextureBlit.cpp b/src/Nazara/Graphics/RenderTextureBlit.cpp index 60b018d4c..4dc8c9766 100644 --- a/src/Nazara/Graphics/RenderTextureBlit.cpp +++ b/src/Nazara/Graphics/RenderTextureBlit.cpp @@ -12,33 +12,33 @@ namespace Nz { - void RenderTextureBlit::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const + std::size_t RenderTextureBlit::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const { - graph.AddOutput(attachmentIndex); - } + std::size_t linkAttachment = graph.AddDummyAttachment(); - void RenderTextureBlit::OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const - { - const std::shared_ptr& sourceTexture = frameGraph.GetAttachmentTexture(finalAttachment); + FramePass& blitPass = graph.AddPass("Blit to texture"); + blitPass.AddInput(attachmentIndex); + blitPass.SetInputAccess(0, TextureLayout::TransferSource, PipelineStage::Transfer, MemoryAccess::MemoryRead); + blitPass.SetInputUsage(0, TextureUsage::TransferSource); - Vector2ui sourceTextureSize = Vector2ui(sourceTexture->GetSize()); - Vector2ui targetTextureSize = Vector2ui(m_targetTexture->GetSize()); + blitPass.AddOutput(linkAttachment); - resources.Execute([&](CommandBufferBuilder& builder) + blitPass.SetCommandCallback([this, attachmentIndex](CommandBufferBuilder& builder, const FramePassEnvironment& env) { - builder.BeginDebugRegion("Blit to texture", Color::Blue()); - { - builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::Transfer, MemoryAccess::ColorWrite, MemoryAccess::TransferRead, TextureLayout::ColorOutput, TextureLayout::TransferSource, *sourceTexture); - builder.TextureBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, {}, MemoryAccess::TransferWrite, TextureLayout::Undefined, TextureLayout::TransferDestination, *m_targetTexture); + const std::shared_ptr& sourceTexture = env.frameGraph.GetAttachmentTexture(attachmentIndex); - Boxui fromBox(0, 0, 0, sourceTextureSize.x, sourceTextureSize.y, 1); - Boxui toBox(0, 0, 0, targetTextureSize.x, targetTextureSize.y, 1); + Vector2ui sourceTextureSize = Vector2ui(sourceTexture->GetSize()); + Vector2ui targetTextureSize = Vector2ui(m_targetTexture->GetSize()); - builder.BlitTexture(*sourceTexture, fromBox, TextureLayout::TransferSource, *m_targetTexture, toBox, TextureLayout::TransferDestination, m_samplerFilter); - builder.TextureBarrier(PipelineStage::Transfer, m_targetPipelineStage, MemoryAccess::TransferWrite, m_targetMemoryFlags, TextureLayout::TransferDestination, m_targetLayout, *m_targetTexture); - } - builder.EndDebugRegion(); - }, QueueType::Graphics); + Boxui fromBox(0, 0, 0, sourceTextureSize.x, sourceTextureSize.y, 1); + Boxui toBox(0, 0, 0, targetTextureSize.x, targetTextureSize.y, 1); + + builder.TextureBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, {}, MemoryAccess::TransferWrite, TextureLayout::Undefined, TextureLayout::TransferDestination, *m_targetTexture); + builder.BlitTexture(*sourceTexture, fromBox, TextureLayout::TransferSource, *m_targetTexture, toBox, TextureLayout::TransferDestination, m_samplerFilter); + builder.TextureBarrier(PipelineStage::Transfer, m_targetPipelineStage, MemoryAccess::TransferWrite, m_targetMemoryFlags, TextureLayout::TransferDestination, m_targetLayout, *m_targetTexture); + }); + + return linkAttachment; } const Vector2ui& RenderTextureBlit::GetSize() const diff --git a/src/Nazara/Graphics/RenderWindow.cpp b/src/Nazara/Graphics/RenderWindow.cpp index b93df17d9..5429e212a 100644 --- a/src/Nazara/Graphics/RenderWindow.cpp +++ b/src/Nazara/Graphics/RenderWindow.cpp @@ -15,6 +15,7 @@ namespace Nz { RenderWindow::RenderWindow(WindowSwapchain& swapchain) : + RenderTarget(DefaultRenderOrder), m_swapchain(nullptr), m_windowSwapchain(&swapchain) { @@ -28,30 +29,40 @@ namespace Nz SetSwapchain(nullptr); }); + SetFrameGraphOutput(true); SetSwapchain(m_windowSwapchain->GetSwapchain()); } - void RenderWindow::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const + std::size_t RenderWindow::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const { - graph.AddOutput(attachmentIndex); - } + // TODO: Replace the blit to swapchain by a graph.BindExternalSwapchain? + std::size_t linkAttachment = graph.AddDummyAttachment(); + + FramePass& blitPass = graph.AddPass("Blit to swapchain"); + blitPass.AddInput(attachmentIndex); + blitPass.SetInputAccess(0, TextureLayout::TransferSource, PipelineStage::Transfer, MemoryAccess::MemoryRead); + blitPass.SetInputUsage(0, TextureUsage::TransferSource); - void RenderWindow::OnRenderEnd(RenderResources& renderResources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const - { - const std::shared_ptr& texture = frameGraph.GetAttachmentTexture(finalAttachment); + blitPass.AddOutput(linkAttachment); - Vector2ui textureSize = Vector2ui(texture->GetSize()); - Boxui blitRegion(0, 0, 0, textureSize.x, textureSize.y, 1); - - renderResources.Execute([&](CommandBufferBuilder& builder) + // Force regeneration of RenderWindow execution callback (since image index changes every frame) + // TODO: Maybe handle this in a better way (temporary command buffer? multiple commands buffers selected by frame index?) + blitPass.SetExecutionCallback([] { - builder.BeginDebugRegion("Blit to swapchain", Color::Blue()); - { - builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::Transfer, MemoryAccess::ColorWrite, MemoryAccess::TransferRead, TextureLayout::ColorOutput, TextureLayout::TransferSource, *texture); - builder.BlitTextureToSwapchain(*texture, blitRegion, TextureLayout::TransferSource, *m_swapchain, renderResources.GetImageIndex()); - } - builder.EndDebugRegion(); - }, QueueType::Graphics); + return FramePassExecution::UpdateAndExecute; + }); + + blitPass.SetCommandCallback([this, attachmentIndex](CommandBufferBuilder& builder, const FramePassEnvironment& env) + { + const std::shared_ptr& sourceTexture = env.frameGraph.GetAttachmentTexture(attachmentIndex); + + Vector2ui textureSize = Vector2ui(sourceTexture->GetSize()); + Boxui blitRegion(0, 0, 0, textureSize.x, textureSize.y, 1); + + builder.BlitTextureToSwapchain(*sourceTexture, blitRegion, TextureLayout::TransferSource, *m_swapchain, env.renderResources.GetImageIndex()); + }); + + return linkAttachment; } const Vector2ui& RenderWindow::GetSize() const