diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 8da726eda..53e87b0f8 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -660,7 +660,6 @@ int main() std::shared_ptr finalBlitBinding; bool lightUpdate = true; - bool matUpdate = false; std::shared_ptr textureSampler = device->InstantiateTextureSampler({}); @@ -838,7 +837,7 @@ int main() gbufferPass.SetExecutionCallback([&] { - return (matUpdate) ? Nz::FramePassExecution::UpdateAndExecute : Nz::FramePassExecution::Execute; + return Nz::FramePassExecution::Execute; }); gbufferPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder, const Nz::Recti& renderArea) @@ -1566,9 +1565,9 @@ int main() builder.CopyBuffer(lightScatteringAllocation, godRaysUBO.get()); } - matUpdate = spaceshipMatPass->Update(frame, builder) || matUpdate; - matUpdate = planeMatPass->Update(frame, builder) || matUpdate; - matUpdate = flareMaterialPass->Update(frame, builder) || matUpdate; + spaceshipMatPass->Update(frame, builder); + planeMatPass->Update(frame, builder); + flareMaterialPass->Update(frame, builder); builder.PostTransferBarrier(); } @@ -1603,7 +1602,6 @@ int main() frame.Present(); - matUpdate = false; lightUpdate = false; // On incrémente le compteur de FPS improvisé diff --git a/include/Nazara/Graphics/DepthPipelinePass.hpp b/include/Nazara/Graphics/DepthPipelinePass.hpp new file mode 100644 index 000000000..b37d48897 --- /dev/null +++ b/include/Nazara/Graphics/DepthPipelinePass.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_DEPTHPIPELINEPASS_HPP +#define NAZARA_GRAPHICS_DEPTHPIPELINEPASS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class AbstractViewer; + class FrameGraph; + class FramePipeline; + class Material; + + class NAZARA_GRAPHICS_API DepthPipelinePass : public FramePipelinePass + { + public: + DepthPipelinePass(FramePipeline& owner, AbstractViewer* viewer); + DepthPipelinePass(const DepthPipelinePass&) = delete; + DepthPipelinePass(DepthPipelinePass&&) = delete; + ~DepthPipelinePass(); + + inline void ForceInvalidation(); + + void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, std::size_t visibilityHash); + + void RegisterMaterial(const Material& material); + void RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex); + + void UnregisterMaterial(const Material& material); + + DepthPipelinePass& operator=(const DepthPipelinePass&) = delete; + DepthPipelinePass& operator=(DepthPipelinePass&&) = delete; + + private: + struct MaterialPassEntry + { + std::size_t usedCount = 1; + + NazaraSlot(MaterialPass, OnMaterialPassPipelineInvalidated, onMaterialPipelineInvalidated); + NazaraSlot(MaterialPass, OnMaterialPassShaderBindingInvalidated, onMaterialShaderBindingInvalidated); + }; + + std::size_t m_depthPassIndex; + std::size_t m_lastVisibilityHash; + std::vector> m_elementRendererData; + std::vector> m_renderElements; + std::vector m_renderStates; + std::unordered_map m_materialPasses; + RenderQueue m_renderQueue; + RenderQueueRegistry m_renderQueueRegistry; + AbstractViewer* m_viewer; + FramePipeline& m_pipeline; + bool m_rebuildCommandBuffer; + bool m_rebuildElements; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_DEPTHPIPELINEPASS_HPP diff --git a/include/Nazara/Graphics/DepthPipelinePass.inl b/include/Nazara/Graphics/DepthPipelinePass.inl new file mode 100644 index 000000000..d275e9d26 --- /dev/null +++ b/include/Nazara/Graphics/DepthPipelinePass.inl @@ -0,0 +1,16 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline void DepthPipelinePass::ForceInvalidation() + { + m_rebuildElements = true; + } +} + +#include diff --git a/include/Nazara/Graphics/DirectionalLight.inl b/include/Nazara/Graphics/DirectionalLight.inl index d0a28a6ac..40f0a48e9 100644 --- a/include/Nazara/Graphics/DirectionalLight.inl +++ b/include/Nazara/Graphics/DirectionalLight.inl @@ -2,7 +2,6 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -#include #include #include #include diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 69ef2f41e..1920fa576 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -37,33 +39,29 @@ namespace Nz ForwardFramePipeline(); ForwardFramePipeline(const ForwardFramePipeline&) = delete; ForwardFramePipeline(ForwardFramePipeline&&) = delete; - ~ForwardFramePipeline() = default; + ~ForwardFramePipeline(); void InvalidateViewer(AbstractViewer* viewerInstance) override; void InvalidateWorldInstance(WorldInstance* worldInstance) override; void RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask) override; void RegisterLight(std::shared_ptr light, UInt32 renderMask) override; + void RegisterMaterialPass(MaterialPass* materialPass) override; void RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override; void Render(RenderFrame& renderFrame) override; void UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) override; void UnregisterLight(Light* light) override; + void UnregisterMaterialPass(MaterialPass* material) override; void UnregisterViewer(AbstractViewer* viewerInstance) override; ForwardFramePipeline& operator=(const ForwardFramePipeline&) = delete; ForwardFramePipeline& operator=(ForwardFramePipeline&&) = delete; - static constexpr std::size_t MaxLightCountPerDraw = 3; - private: BakedFrameGraph BuildFrameGraph(); - void RegisterMaterialPass(MaterialPass* material); - template void ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback); - void UnregisterMaterialPass(MaterialPass* material); - struct ViewerData; struct LightData @@ -74,30 +72,11 @@ namespace Nz NazaraSlot(Light, OnLightDataInvalided, onLightInvalidated); }; - using LightKey = std::array; - - struct LightKeyHasher - { - inline std::size_t operator()(const LightKey& lightKey) const; - }; - - struct LightDataUbo - { - std::shared_ptr renderBuffer; - std::size_t offset = 0; - UploadPool::Allocation* allocation = nullptr; - }; - - struct LightUboPool - { - std::vector> lightUboBuffers; - }; - - struct MaterialData + struct MaterialPassData { std::size_t usedCount = 0; - NazaraSlot(MaterialPass, OnMaterialInvalidated, onMaterialInvalided); + NazaraSlot(MaterialPass, OnMaterialPassInvalidated, onMaterialPassInvalided); }; struct RenderableData @@ -115,51 +94,31 @@ namespace Nz ShaderBindingPtr blitShaderBinding; }; - struct VisibleRenderable - { - const InstancedRenderable* instancedRenderable; - const WorldInstance* worldInstance; - }; - struct ViewerData { std::size_t colorAttachment; std::size_t depthStencilAttachment; - std::size_t visibilityHash = 0; - std::unordered_map lightPerRenderElement; - std::unordered_map lightBufferPerLights; - std::vector> depthPrepassRenderElements; - std::vector> forwardRenderElements; - std::vector> elementRendererData; + std::unique_ptr depthPrepass; + std::unique_ptr forwardPass; Int32 renderOrder = 0; - RenderQueueRegistry depthPrepassRegistry; RenderQueueRegistry forwardRegistry; - RenderQueue depthPrepassRenderQueue; RenderQueue forwardRenderQueue; ShaderBindingPtr blitShaderBinding; - bool prepare = true; - bool rebuildDepthPrepass = true; - bool rebuildForwardPass = true; }; - std::size_t m_depthPassIndex; std::size_t m_forwardPassIndex; - std::shared_ptr m_lightUboPool; std::unordered_map m_viewers; std::unordered_map m_lights; - std::unordered_map m_materials; + std::unordered_map m_activeMaterialPasses; std::unordered_map> m_renderables; std::unordered_map m_renderTargets; std::unordered_set m_invalidatedViewerInstances; - std::unordered_set m_invalidatedMaterials; + std::unordered_set m_invalidatedMaterialPasses; std::unordered_set m_invalidatedWorldInstances; std::unordered_set m_removedWorldInstances; - std::vector> m_elementRenderers; std::vector m_renderStates; - std::vector m_renderableLights; + std::vector m_visibleRenderables; std::vector m_visibleLights; - std::vector m_lightDataBuffers; - std::vector m_visibleRenderables; BakedFrameGraph m_bakedFrameGraph; RenderFrame* m_currentRenderFrame; bool m_rebuildFrameGraph; diff --git a/include/Nazara/Graphics/ForwardFramePipeline.inl b/include/Nazara/Graphics/ForwardFramePipeline.inl index 8662726b6..47b5ce6f6 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.inl +++ b/include/Nazara/Graphics/ForwardFramePipeline.inl @@ -8,20 +8,6 @@ namespace Nz { - inline std::size_t ForwardFramePipeline::LightKeyHasher::operator()(const LightKey& lightKey) const - { - std::size_t lightHash = 5; - auto CombineHash = [](std::size_t currentHash, std::size_t newHash) - { - return currentHash * 23 + newHash; - }; - - std::hash lightPtrHasher; - for (std::size_t i = 0; i < lightKey.size(); ++i) - lightHash = CombineHash(lightHash, lightPtrHasher(lightKey[i])); - - return lightHash; - } } #include diff --git a/include/Nazara/Graphics/ForwardPipelinePass.hpp b/include/Nazara/Graphics/ForwardPipelinePass.hpp new file mode 100644 index 000000000..a873aa170 --- /dev/null +++ b/include/Nazara/Graphics/ForwardPipelinePass.hpp @@ -0,0 +1,102 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_FORWARDPIPELINEPASS_HPP +#define NAZARA_GRAPHICS_FORWARDPIPELINEPASS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class AbstractViewer; + class FrameGraph; + class FramePipeline; + class Light; + class Material; + + class NAZARA_GRAPHICS_API ForwardPipelinePass : public FramePipelinePass + { + public: + ForwardPipelinePass(FramePipeline& owner, AbstractViewer* viewer); + ForwardPipelinePass(const ForwardPipelinePass&) = delete; + ForwardPipelinePass(ForwardPipelinePass&&) = delete; + ~ForwardPipelinePass(); + + inline void ForceInvalidation(); + + void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash); + + void RegisterMaterial(const Material& material); + void RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex); + + void UnregisterMaterial(const Material& material); + + ForwardPipelinePass& operator=(const ForwardPipelinePass&) = delete; + ForwardPipelinePass& operator=(ForwardPipelinePass&&) = delete; + + static constexpr std::size_t MaxLightCountPerDraw = 3; + + private: + struct MaterialPassEntry + { + std::size_t usedCount = 1; + + NazaraSlot(MaterialPass, OnMaterialPassPipelineInvalidated, onMaterialPipelineInvalidated); + NazaraSlot(MaterialPass, OnMaterialPassShaderBindingInvalidated, onMaterialShaderBindingInvalidated); + }; + + using LightKey = std::array; + + struct LightKeyHasher + { + inline std::size_t operator()(const LightKey& lightKey) const; + }; + + struct LightDataUbo + { + std::shared_ptr renderBuffer; + std::size_t offset = 0; + UploadPool::Allocation* allocation = nullptr; + }; + + struct LightUboPool + { + std::vector> lightUboBuffers; + }; + + std::size_t m_forwardPassIndex; + std::size_t m_lastVisibilityHash; + std::shared_ptr m_lightUboPool; + std::vector> m_elementRendererData; + std::vector> m_renderElements; + std::vector m_renderStates; + std::unordered_map m_materialPasses; + std::unordered_map m_lightPerRenderElement; + std::unordered_map m_lightBufferPerLights; + std::vector m_lightDataBuffers; + std::vector m_renderableLights; + RenderQueue m_renderQueue; + RenderQueueRegistry m_renderQueueRegistry; + AbstractViewer* m_viewer; + FramePipeline& m_pipeline; + bool m_rebuildCommandBuffer; + bool m_rebuildElements; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_FORWARDPIPELINEPASS_HPP diff --git a/include/Nazara/Graphics/ForwardPipelinePass.inl b/include/Nazara/Graphics/ForwardPipelinePass.inl new file mode 100644 index 000000000..fc00815b0 --- /dev/null +++ b/include/Nazara/Graphics/ForwardPipelinePass.inl @@ -0,0 +1,31 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline void ForwardPipelinePass::ForceInvalidation() + { + m_rebuildElements = true; + } + + inline std::size_t ForwardPipelinePass::LightKeyHasher::operator()(const LightKey& lightKey) const + { + std::size_t lightHash = 5; + auto CombineHash = [](std::size_t currentHash, std::size_t newHash) + { + return currentHash * 23 + newHash; + }; + + std::hash lightPtrHasher; + for (std::size_t i = 0; i < lightKey.size(); ++i) + lightHash = CombineHash(lightHash, lightPtrHasher(lightKey[i])); + + return lightHash; + } +} + +#include diff --git a/include/Nazara/Graphics/FramePipeline.hpp b/include/Nazara/Graphics/FramePipeline.hpp index d9fd43a9b..d6e22f926 100644 --- a/include/Nazara/Graphics/FramePipeline.hpp +++ b/include/Nazara/Graphics/FramePipeline.hpp @@ -9,38 +9,56 @@ #include #include +#include +#include #include +#include +#include namespace Nz { class AbstractViewer; + class ElementRenderer; class InstancedRenderable; class Light; + class MaterialPass; class RenderFrame; class NAZARA_GRAPHICS_API FramePipeline { public: - FramePipeline() = default; + FramePipeline(); FramePipeline(const FramePipeline&) = delete; FramePipeline(FramePipeline&&) noexcept = default; virtual ~FramePipeline(); + template void ForEachElementRenderer(F&& callback); + + inline ElementRenderer& GetElementRenderer(std::size_t elementIndex); + inline std::size_t GetElementRendererCount() const; + virtual void InvalidateViewer(AbstractViewer* viewerInstance) = 0; virtual void InvalidateWorldInstance(WorldInstance* worldInstance) = 0; + template void ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback); + virtual void RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask) = 0; virtual void RegisterLight(std::shared_ptr light, UInt32 renderMask) = 0; + virtual void RegisterMaterialPass(MaterialPass* materialPass) = 0; virtual void RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0; virtual void Render(RenderFrame& renderFrame) = 0; virtual void UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) = 0; virtual void UnregisterLight(Light* light) = 0; + virtual void UnregisterMaterialPass(MaterialPass* materialPass) = 0; virtual void UnregisterViewer(AbstractViewer* viewerInstance) = 0; FramePipeline& operator=(const FramePipeline&) = delete; FramePipeline& operator=(FramePipeline&&) noexcept = default; + + private: + std::vector> m_elementRenderers; }; } diff --git a/include/Nazara/Graphics/FramePipeline.inl b/include/Nazara/Graphics/FramePipeline.inl index 472fa0a45..cce001ab2 100644 --- a/include/Nazara/Graphics/FramePipeline.inl +++ b/include/Nazara/Graphics/FramePipeline.inl @@ -3,10 +3,58 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include namespace Nz { + inline ElementRenderer& FramePipeline::GetElementRenderer(std::size_t elementIndex) + { + assert(elementIndex < m_elementRenderers.size()); + return *m_elementRenderers[elementIndex]; + } + + inline std::size_t FramePipeline::GetElementRendererCount() const + { + return m_elementRenderers.size(); + } + + template + void FramePipeline::ForEachElementRenderer(F&& callback) + { + for (std::size_t i = 0; i < m_elementRenderers.size(); ++i) + { + if (m_elementRenderers[i]) + callback(i, *m_elementRenderers[i]); + } + } + + template + void FramePipeline::ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback) + { + if (renderQueue.empty()) + return; + + auto it = renderQueue.begin(); + auto itEnd = renderQueue.end(); + while (it != itEnd) + { + const RenderElement* element = *it; + UInt8 elementType = element->GetElementType(); + + const Pointer* first = it; + + ++it; + while (it != itEnd && (*it)->GetElementType() == elementType) + ++it; + + std::size_t count = it - first; + if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType]) + continue; + + callback(elementType, first, count); + } + } } #include diff --git a/include/Nazara/Graphics/FramePipelinePass.hpp b/include/Nazara/Graphics/FramePipelinePass.hpp new file mode 100644 index 000000000..1e5654b84 --- /dev/null +++ b/include/Nazara/Graphics/FramePipelinePass.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_FRAMEPIPELINEPASS_HPP +#define NAZARA_GRAPHICS_FRAMEPIPELINEPASS_HPP + +#include +#include + +namespace Nz +{ + class InstancedRenderable; + class WorldInstance; + + class NAZARA_GRAPHICS_API FramePipelinePass + { + public: + FramePipelinePass() = default; + FramePipelinePass(const FramePipelinePass&) = delete; + FramePipelinePass(FramePipelinePass&&) = delete; + virtual ~FramePipelinePass(); + + FramePipelinePass& operator=(const FramePipelinePass&) = delete; + FramePipelinePass& operator=(FramePipelinePass&&) = delete; + + struct VisibleRenderable + { + const InstancedRenderable* instancedRenderable; + const WorldInstance* worldInstance; + }; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_FRAMEPIPELINEPASS_HPP diff --git a/include/Nazara/Graphics/FramePipelinePass.inl b/include/Nazara/Graphics/FramePipelinePass.inl new file mode 100644 index 000000000..6d56e6595 --- /dev/null +++ b/include/Nazara/Graphics/FramePipelinePass.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Graphics/Material.hpp b/include/Nazara/Graphics/Material.hpp index 98f01e0eb..7de4323d8 100644 --- a/include/Nazara/Graphics/Material.hpp +++ b/include/Nazara/Graphics/Material.hpp @@ -16,7 +16,7 @@ namespace Nz class NAZARA_GRAPHICS_API Material : public Resource { public: - Material(); + Material() = default; ~Material() = default; inline void AddPass(std::size_t passIndex, std::shared_ptr pass); @@ -24,6 +24,8 @@ namespace Nz inline const std::shared_ptr& FindPass(const std::string& passName) const; + template void ForEachPass(F&& callback); + inline const std::shared_ptr& GetPass(std::size_t passIndex) const; inline bool HasPass(std::size_t passIndex) const; diff --git a/include/Nazara/Graphics/Material.inl b/include/Nazara/Graphics/Material.inl index b5f5553a8..b7694d9de 100644 --- a/include/Nazara/Graphics/Material.inl +++ b/include/Nazara/Graphics/Material.inl @@ -27,6 +27,16 @@ namespace Nz return GetPass(registry.GetPassIndex(passName)); } + template + void Material::ForEachPass(F&& callback) + { + for (std::size_t i = 0; i < m_passes.size(); ++i) + { + if (m_passes[i]) + callback(i, m_passes[i]); + } + } + inline const std::shared_ptr& Material::GetPass(std::size_t passIndex) const { if (passIndex >= m_passes.size()) diff --git a/include/Nazara/Graphics/MaterialPass.hpp b/include/Nazara/Graphics/MaterialPass.hpp index 2b9a23a7d..7457772c7 100644 --- a/include/Nazara/Graphics/MaterialPass.hpp +++ b/include/Nazara/Graphics/MaterialPass.hpp @@ -103,15 +103,17 @@ namespace Nz inline void SetTextureSampler(std::size_t textureIndex, TextureSamplerInfo samplerInfo); inline void SetUniformBuffer(std::size_t bufferIndex, std::shared_ptr uniformBuffer); - bool Update(RenderFrame& renderFrame, CommandBufferBuilder& builder); + void Update(RenderFrame& renderFrame, CommandBufferBuilder& builder); // Signals: - NazaraSignal(OnMaterialInvalidated, const MaterialPass* /*material*/); - NazaraSignal(OnMaterialRelease, const MaterialPass* /*material*/); + NazaraSignal(OnMaterialPassInvalidated, const MaterialPass* /*materialPass*/); + NazaraSignal(OnMaterialPassPipelineInvalidated, const MaterialPass* /*materialPass*/); + NazaraSignal(OnMaterialPassShaderBindingInvalidated, const MaterialPass* /*materialPass*/); + NazaraSignal(OnMaterialPassRelease, const MaterialPass* /*materialPass*/); private: - inline void InvalidateCommandBuffer(); inline void InvalidatePipeline(); + inline void InvalidateShaderBinding(); inline void InvalidateTextureSampler(std::size_t textureIndex); inline void InvalidateUniformData(std::size_t uniformBufferIndex); void UpdatePipeline() const; @@ -137,7 +139,6 @@ namespace Nz mutable std::shared_ptr m_pipeline; mutable MaterialPipelineInfo m_pipelineInfo; MaterialPassFlags m_flags; - bool m_forceCommandBufferRegeneration; mutable bool m_pipelineUpdated; }; } diff --git a/include/Nazara/Graphics/MaterialPass.inl b/include/Nazara/Graphics/MaterialPass.inl index 3a732d2a2..ef4806657 100644 --- a/include/Nazara/Graphics/MaterialPass.inl +++ b/include/Nazara/Graphics/MaterialPass.inl @@ -16,7 +16,7 @@ namespace Nz */ inline MaterialPass::~MaterialPass() { - OnMaterialRelease(this); + OnMaterialPassRelease(this); } /*! @@ -609,7 +609,7 @@ namespace Nz { m_textures[textureIndex].texture = std::move(texture); - InvalidateCommandBuffer(); + InvalidateShaderBinding(); } } @@ -632,22 +632,20 @@ namespace Nz m_uniformBuffers[bufferIndex].buffer = std::move(uniformBuffer); m_uniformBuffers[bufferIndex].dataInvalidated = true; - InvalidateCommandBuffer(); + InvalidateShaderBinding(); } } - inline void MaterialPass::InvalidateCommandBuffer() - { - m_forceCommandBufferRegeneration = true; - OnMaterialInvalidated(this); - } - inline void MaterialPass::InvalidatePipeline() { - m_forceCommandBufferRegeneration = true; m_pipelineUpdated = false; - OnMaterialInvalidated(this); + OnMaterialPassPipelineInvalidated(this); + } + + inline void MaterialPass::InvalidateShaderBinding() + { + OnMaterialPassShaderBindingInvalidated(this); } inline void MaterialPass::InvalidateTextureSampler(std::size_t textureIndex) @@ -655,7 +653,7 @@ namespace Nz assert(textureIndex < m_textures.size()); m_textures[textureIndex].sampler.reset(); - InvalidateCommandBuffer(); + InvalidateShaderBinding(); } inline void MaterialPass::InvalidateUniformData(std::size_t uniformBufferIndex) @@ -664,7 +662,7 @@ namespace Nz UniformBuffer& uboEntry = m_uniformBuffers[uniformBufferIndex]; uboEntry.dataInvalidated = true; - OnMaterialInvalidated(this); + OnMaterialPassInvalidated(this); } } diff --git a/src/Nazara/Graphics/DepthPipelinePass.cpp b/src/Nazara/Graphics/DepthPipelinePass.cpp new file mode 100644 index 000000000..e9256fceb --- /dev/null +++ b/src/Nazara/Graphics/DepthPipelinePass.cpp @@ -0,0 +1,179 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + DepthPipelinePass::DepthPipelinePass(FramePipeline& owner, AbstractViewer* viewer) : + m_lastVisibilityHash(0), + m_viewer(viewer), + m_pipeline(owner), + m_rebuildCommandBuffer(false), + m_rebuildElements(false) + { + m_depthPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("DepthPass"); + } + + DepthPipelinePass::~DepthPipelinePass() + { + for (auto&& [materialPass, entry] : m_materialPasses) + m_pipeline.UnregisterMaterialPass(materialPass); + } + + void DepthPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, std::size_t visibilityHash) + { + if (m_lastVisibilityHash != visibilityHash) + { + renderFrame.PushForRelease(std::move(m_renderElements)); + m_renderElements.clear(); + + for (const auto& renderableData : visibleRenderables) + renderableData.instancedRenderable->BuildElement(m_depthPassIndex, *renderableData.worldInstance, m_renderElements); + + m_renderQueueRegistry.Clear(); + m_renderQueue.Clear(); + + for (const auto& renderElement : m_renderElements) + { + renderElement->Register(m_renderQueueRegistry); + m_renderQueue.Insert(renderElement.get()); + } + + m_renderQueueRegistry.Finalize(); + + m_lastVisibilityHash = visibilityHash; + m_rebuildElements = true; + } + + // TODO: Don't sort every frame if no material pass requires distance sorting + m_renderQueue.Sort([&](const RenderElement* element) + { + return element->ComputeSortingScore(frustum, m_renderQueueRegistry); + }); + + if (m_rebuildElements) + { + m_pipeline.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) + { + if (elementType >= m_elementRendererData.size() || !m_elementRendererData[elementType]) + { + if (elementType >= m_elementRendererData.size()) + m_elementRendererData.resize(elementType + 1); + + m_elementRendererData[elementType] = elementRenderer.InstanciateData(); + } + + elementRenderer.Reset(*m_elementRendererData[elementType], renderFrame); + }); + + const auto& viewerInstance = m_viewer->GetViewerInstance(); + + m_pipeline.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = m_pipeline.GetElementRenderer(elementType); + + m_renderStates.clear(); + m_renderStates.resize(elementCount); + + elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data()); + }); + + m_pipeline.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) + { + elementRenderer.PrepareEnd(renderFrame, *m_elementRendererData[elementType]); + }); + + m_rebuildCommandBuffer = true; + m_rebuildElements = false; + } + } + + void DepthPipelinePass::RegisterMaterial(const Material& material) + { + if (!material.HasPass(m_depthPassIndex)) + return; + + MaterialPass* materialPass = material.GetPass(m_depthPassIndex).get(); + + auto it = m_materialPasses.find(materialPass); + if (it == m_materialPasses.end()) + { + m_pipeline.RegisterMaterialPass(materialPass); + + auto& matPassEntry = m_materialPasses[materialPass]; + matPassEntry.onMaterialPipelineInvalidated.Connect(materialPass->OnMaterialPassPipelineInvalidated, [=](const MaterialPass*) + { + m_rebuildElements = true; + }); + + matPassEntry.onMaterialShaderBindingInvalidated.Connect(materialPass->OnMaterialPassShaderBindingInvalidated, [=](const MaterialPass*) + { + m_rebuildCommandBuffer = true; + }); + } + else + it->second.usedCount++; + } + + void DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex) + { + FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass"); + depthPrepass.SetDepthStencilOutput(depthBufferIndex); + depthPrepass.SetDepthStencilClear(1.f, 0); + + depthPrepass.SetExecutionCallback([&]() + { + if (m_rebuildCommandBuffer) + return FramePassExecution::UpdateAndExecute; + else + return FramePassExecution::Execute; + }); + + depthPrepass.SetCommandCallback([this](CommandBufferBuilder& builder, const Recti& /*renderRect*/) + { + Recti viewport = m_viewer->GetViewport(); + + builder.SetScissor(viewport); + builder.SetViewport(viewport); + + const auto& viewerInstance = m_viewer->GetViewerInstance(); + + m_pipeline.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = m_pipeline.GetElementRenderer(elementType); + elementRenderer.Render(viewerInstance, *m_elementRendererData[elementType], builder, elementCount, elements); + }); + + m_rebuildCommandBuffer = false; + }); + } + + void DepthPipelinePass::UnregisterMaterial(const Material& material) + { + if (!material.HasPass(m_depthPassIndex)) + return; + + MaterialPass* materialPass = material.GetPass(m_depthPassIndex).get(); + + auto it = m_materialPasses.find(materialPass); + if (it != m_materialPasses.end()) + { + if (--it->second.usedCount == 0) + { + m_pipeline.UnregisterMaterialPass(materialPass); + m_materialPasses.erase(it); + } + } + } +} diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index d00a2f6d1..a702ed106 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include #include #include @@ -32,14 +30,13 @@ namespace Nz m_rebuildFrameGraph(true) { auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry(); - m_depthPassIndex = passRegistry.GetPassIndex("DepthPass"); m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass"); + } - m_elementRenderers.resize(BasicRenderElementCount); - m_elementRenderers[UnderlyingCast(BasicRenderElement::SpriteChain)] = std::make_unique(*Graphics::Instance()->GetRenderDevice()); - m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique(); - - m_lightUboPool = std::make_shared(); + ForwardFramePipeline::~ForwardFramePipeline() + { + // Force viewer passes to unregister their materials + m_viewers.clear(); } void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance) @@ -64,15 +61,21 @@ namespace Nz { auto& renderableData = renderableMap.emplace(instancedRenderable, RenderableData{}).first->second; renderableData.renderMask = renderMask; - - renderableData.onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [this](InstancedRenderable* /*instancedRenderable*/) + + renderableData.onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [=](InstancedRenderable* /*instancedRenderable*/) { - // TODO: Invalidate only relevant viewers + // TODO: Invalidate only relevant viewers and passes for (auto&& [viewer, viewerData] : m_viewers) { - viewerData.rebuildDepthPrepass = true; - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; + UInt32 viewerRenderMask = viewer->GetRenderMask(); + + if (viewerRenderMask & renderMask) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->ForceInvalidation(); + + viewerData.forwardPass->ForceInvalidation(); + } } }); @@ -80,29 +83,25 @@ namespace Nz { if (newMaterial) { - if (const auto& pass = newMaterial->GetPass(m_depthPassIndex)) - RegisterMaterialPass(pass.get()); + for (auto&& [viewer, viewerData] : m_viewers) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->RegisterMaterial(*newMaterial); - if (const auto& pass = newMaterial->GetPass(m_forwardPassIndex)) - RegisterMaterialPass(pass.get()); + viewerData.forwardPass->RegisterMaterial(*newMaterial); + } } const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex); if (prevMaterial) { - if (const auto& pass = prevMaterial->GetPass(m_depthPassIndex)) - UnregisterMaterialPass(pass.get()); + for (auto&& [viewer, viewerData] : m_viewers) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->UnregisterMaterial(*prevMaterial); - if (const auto& pass = prevMaterial->GetPass(m_forwardPassIndex)) - UnregisterMaterialPass(pass.get()); - } - - // TODO: Invalidate only relevant viewers - for (auto&& [viewer, viewerData] : m_viewers) - { - viewerData.rebuildDepthPrepass = true; - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; + viewerData.forwardPass->UnregisterMaterial(*prevMaterial); + } } }); @@ -111,21 +110,13 @@ namespace Nz { if (Material* mat = instancedRenderable->GetMaterial(i).get()) { - if (const auto& pass = mat->GetPass(m_depthPassIndex)) - RegisterMaterialPass(pass.get()); + for (auto&& [viewer, viewerData] : m_viewers) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->RegisterMaterial(*mat); - if (const auto& pass = mat->GetPass(m_forwardPassIndex)) - RegisterMaterialPass(pass.get()); - } - } - - for (auto&& [viewer, viewerData] : m_viewers) - { - if (viewer->GetRenderMask() & renderMask) - { - viewerData.rebuildDepthPrepass = true; - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; + viewerData.forwardPass->RegisterMaterial(*mat); + } } } } @@ -136,20 +127,42 @@ namespace Nz auto& lightData = m_lights[light.get()]; lightData.light = std::move(light); lightData.renderMask = renderMask; - lightData.onLightInvalidated.Connect(lightData.light->OnLightDataInvalided, [this](Light*) + lightData.onLightInvalidated.Connect(lightData.light->OnLightDataInvalided, [=](Light*) { + //TODO: Switch lights to storage buffers so they can all be part of GPU memory for (auto&& [viewer, viewerData] : m_viewers) { - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; + UInt32 viewerRenderMask = viewer->GetRenderMask(); + + if (viewerRenderMask & renderMask) + viewerData.forwardPass->ForceInvalidation(); } }); } + void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* materialPass) + { + auto it = m_activeMaterialPasses.find(materialPass); + if (it == m_activeMaterialPasses.end()) + { + it = m_activeMaterialPasses.emplace(materialPass, MaterialPassData{}).first; + it->second.onMaterialPassInvalided.Connect(materialPass->OnMaterialPassInvalidated, [=](const MaterialPass* /*material*/) + { + m_invalidatedMaterialPasses.insert(materialPass); + }); + + m_invalidatedMaterialPasses.insert(materialPass); + } + + it->second.usedCount++; + } + void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) { auto& viewerData = m_viewers.emplace(viewerInstance, ViewerData{}).first->second; viewerData.renderOrder = renderOrder; + viewerData.depthPrepass = std::make_unique(*this, viewerInstance); + viewerData.forwardPass = std::make_unique(*this, viewerInstance); m_invalidatedViewerInstances.insert(viewerInstance); m_rebuildFrameGraph = true; @@ -189,19 +202,10 @@ namespace Nz m_invalidatedWorldInstances.clear(); - for (MaterialPass* material : m_invalidatedMaterials) - { - if (material->Update(renderFrame, builder)) - { - for (auto&& [viewer, viewerData] : m_viewers) - { - viewerData.rebuildDepthPrepass = true; - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; - } - } - } - m_invalidatedMaterials.clear(); + for (MaterialPass* materialPass : m_invalidatedMaterialPasses) + materialPass->Update(renderFrame, builder); + + m_invalidatedMaterialPasses.clear(); builder.PostTransferBarrier(); } @@ -213,9 +217,6 @@ namespace Nz return currentHash * 23 + newHash; }; - PredefinedLightData lightOffsets = PredefinedLightData::GetOffsets(); - std::size_t lightUboAlignedSize = Align(lightOffsets.totalSize, graphics->GetRenderDevice()->GetDeviceInfo().limits.minUniformBufferOffsetAlignment); - // Render queues handling for (auto&& [viewer, data] : m_viewers) { @@ -259,7 +260,9 @@ namespace Nz visibilityHash = CombineHash(visibilityHash, std::hash()(worldInstance.get())); } - // TODO: Lights update shouldn't trigger a rebuild of the depth prepass + // Lights update don't trigger a rebuild of the depth pre-pass + std::size_t depthVisibilityHash = visibilityHash; + m_visibleLights.clear(); for (auto&& [light, lightData] : m_lights) { @@ -273,245 +276,10 @@ namespace Nz } } - if (viewerData.visibilityHash != visibilityHash) - { - viewerData.rebuildDepthPrepass = true; - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; + if (viewerData.depthPrepass) + viewerData.depthPrepass->Prepare(renderFrame, frustum, m_visibleRenderables, depthVisibilityHash); - viewerData.visibilityHash = visibilityHash; - } - - if (viewerData.rebuildDepthPrepass) - { - renderFrame.PushForRelease(std::move(viewerData.depthPrepassRenderElements)); - viewerData.depthPrepassRenderElements.clear(); - - for (const auto& renderableData : m_visibleRenderables) - renderableData.instancedRenderable->BuildElement(m_depthPassIndex, *renderableData.worldInstance, viewerData.depthPrepassRenderElements); - - viewerData.depthPrepassRegistry.Clear(); - viewerData.depthPrepassRenderQueue.Clear(); - - for (const auto& renderElement : viewerData.depthPrepassRenderElements) - { - renderElement->Register(viewerData.depthPrepassRegistry); - viewerData.depthPrepassRenderQueue.Insert(renderElement.get()); - } - - viewerData.depthPrepassRegistry.Finalize(); - } - - viewerData.depthPrepassRenderQueue.Sort([&](const RenderElement* element) - { - return element->ComputeSortingScore(frustum, viewerData.depthPrepassRegistry); - }); - - if (viewerData.rebuildForwardPass) - { - renderFrame.PushForRelease(std::move(viewerData.forwardRenderElements)); - viewerData.forwardRenderElements.clear(); - viewerData.forwardRegistry.Clear(); - viewerData.forwardRenderQueue.Clear(); - viewerData.lightBufferPerLights.clear(); - viewerData.lightPerRenderElement.clear(); - - for (auto& lightDataUbo : m_lightDataBuffers) - { - renderFrame.PushReleaseCallback([pool = m_lightUboPool, lightUbo = std::move(lightDataUbo.renderBuffer)]() - { - pool->lightUboBuffers.push_back(std::move(lightUbo)); - }); - } - m_lightDataBuffers.clear(); - - for (const auto& renderableData : m_visibleRenderables) - { - BoundingVolumef renderableBoundingVolume(renderableData.instancedRenderable->GetAABB()); - renderableBoundingVolume.Update(renderableData.worldInstance->GetWorldMatrix()); - - // Select lights (TODO: Cull lights in frustum) - m_renderableLights.clear(); - for (const Light* light : m_visibleLights) - { - const BoundingVolumef& boundingVolume = light->GetBoundingVolume(); - if (boundingVolume.Intersect(renderableBoundingVolume.aabb)) - m_renderableLights.push_back(light); - } - - // Sort lights - std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const Light* lhs, const Light* rhs) - { - return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume); - }); - - std::size_t lightCount = std::min(m_renderableLights.size(), MaxLightCountPerDraw); - - LightKey lightKey; - lightKey.fill(nullptr); - for (std::size_t i = 0; i < lightCount; ++i) - lightKey[i] = m_renderableLights[i]; - - RenderBufferView lightUboView; - - auto it = viewerData.lightBufferPerLights.find(lightKey); - if (it == viewerData.lightBufferPerLights.end()) - { - // Prepare light ubo upload - - // Find light ubo - LightDataUbo* targetLightData = nullptr; - for (auto& lightUboData : m_lightDataBuffers) - { - if (lightUboData.offset + lightUboAlignedSize <= lightUboData.renderBuffer->GetSize()) - { - targetLightData = &lightUboData; - break; - } - } - - if (!targetLightData) - { - // Make a new light UBO - auto& lightUboData = m_lightDataBuffers.emplace_back(); - - // Reuse from pool if possible - if (!m_lightUboPool->lightUboBuffers.empty()) - { - lightUboData.renderBuffer = m_lightUboPool->lightUboBuffers.back(); - m_lightUboPool->lightUboBuffers.pop_back(); - } - else - lightUboData.renderBuffer = graphics->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, 256 * lightUboAlignedSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write); - - targetLightData = &lightUboData; - } - - assert(targetLightData); - if (!targetLightData->allocation) - targetLightData->allocation = &uploadPool.Allocate(targetLightData->renderBuffer->GetSize()); - - void* lightDataPtr = static_cast(targetLightData->allocation->mappedPtr) + targetLightData->offset; - AccessByOffset(lightDataPtr, lightOffsets.lightCountOffset) = SafeCast(lightCount); - - UInt8* lightPtr = static_cast(lightDataPtr) + lightOffsets.lightsOffset; - for (std::size_t i = 0; i < lightCount; ++i) - { - m_renderableLights[i]->FillLightData(lightPtr); - lightPtr += lightOffsets.lightSize; - } - - // Associate render element with light ubo - lightUboView = RenderBufferView(targetLightData->renderBuffer.get(), targetLightData->offset, lightUboAlignedSize); - - targetLightData->offset += lightUboAlignedSize; - - viewerData.lightBufferPerLights.emplace(lightKey, lightUboView); - } - else - lightUboView = it->second; - - std::size_t previousCount = viewerData.forwardRenderElements.size(); - renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, viewerData.forwardRenderElements); - for (std::size_t i = previousCount; i < viewerData.forwardRenderElements.size(); ++i) - { - const RenderElement* element = viewerData.forwardRenderElements[i].get(); - viewerData.lightPerRenderElement.emplace(element, lightUboView); - } - } - - for (const auto& renderElement : viewerData.forwardRenderElements) - { - renderElement->Register(viewerData.forwardRegistry); - viewerData.forwardRenderQueue.Insert(renderElement.get()); - } - - viewerData.forwardRegistry.Finalize(); - - renderFrame.Execute([&](CommandBufferBuilder& builder) - { - builder.BeginDebugRegion("Light UBO Update", Color::Yellow); - { - for (auto& lightUboData : m_lightDataBuffers) - { - if (!lightUboData.allocation) - continue; - - builder.CopyBuffer(*lightUboData.allocation, RenderBufferView(lightUboData.renderBuffer.get(), 0, lightUboData.offset)); - } - - builder.PostTransferBarrier(); - } - builder.EndDebugRegion(); - }, QueueType::Transfer); - } - - viewerData.forwardRenderQueue.Sort([&](const RenderElement* element) - { - return element->ComputeSortingScore(frustum, viewerData.forwardRegistry); - }); - } - - for (auto&& [viewer, viewerData] : m_viewers) - { - if (!viewerData.prepare) - continue; - - for (std::size_t i = 0; i < m_elementRenderers.size(); ++i) - { - auto& elementRendererPtr = m_elementRenderers[i]; - - if (i >= viewerData.elementRendererData.size() || !viewerData.elementRendererData[i]) - { - if (i >= viewerData.elementRendererData.size()) - viewerData.elementRendererData.resize(i + 1); - - viewerData.elementRendererData[i] = elementRendererPtr->InstanciateData(); - } - - if (elementRendererPtr) - elementRendererPtr->Reset(*viewerData.elementRendererData[i], renderFrame); - } - - auto& rendererData = viewerData.elementRendererData; - - const auto& viewerInstance = viewer->GetViewerInstance(); - - ProcessRenderQueue(viewerData.depthPrepassRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) - { - ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; - - m_renderStates.clear(); - m_renderStates.resize(elementCount); - - elementRenderer.Prepare(viewerInstance, *rendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data()); - }); - - for (std::size_t i = 0; i < m_elementRenderers.size(); ++i) - m_elementRenderers[i]->PrepareEnd(renderFrame, *rendererData[i]); - - auto& lightPerRenderElement = viewerData.lightPerRenderElement; - ProcessRenderQueue(viewerData.forwardRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) - { - ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; - - m_renderStates.clear(); - - m_renderStates.reserve(elementCount); - for (std::size_t i = 0; i < elementCount; ++i) - { - auto it = lightPerRenderElement.find(elements[i]); - assert(it != lightPerRenderElement.end()); - - auto& renderStates = m_renderStates.emplace_back(); - renderStates.lightData = it->second; - } - - elementRenderer.Prepare(viewerInstance, *rendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data()); - }); - - for (std::size_t i = 0; i < m_elementRenderers.size(); ++i) - m_elementRenderers[i]->PrepareEnd(renderFrame, *rendererData[i]); + viewerData.forwardPass->Prepare(renderFrame, frustum, m_visibleRenderables, m_visibleLights, visibilityHash); } if (m_bakedFrameGraph.Resize(renderFrame)) @@ -555,13 +323,7 @@ namespace Nz m_bakedFrameGraph.Execute(renderFrame); m_rebuildFrameGraph = false; - for (auto&& [viewer, viewerData] : m_viewers) - { - viewerData.rebuildForwardPass = false; - viewerData.rebuildDepthPrepass = false; - viewerData.prepare = false; - } - + // Final blit (TODO: Make part of frame graph) const Vector2ui& frameSize = renderFrame.GetSize(); for (auto&& [renderTargetPtr, renderTargetData] : m_renderTargets) { @@ -623,30 +385,31 @@ namespace Nz std::size_t matCount = instancedRenderable->GetMaterialCount(); for (std::size_t i = 0; i < matCount; ++i) { - if (const auto& pass = instancedRenderable->GetMaterial(i)->GetPass(m_depthPassIndex)) - UnregisterMaterialPass(pass.get()); + for (auto&& [viewer, viewerData] : m_viewers) + { + const auto& material = instancedRenderable->GetMaterial(i); + if (viewerData.depthPrepass) + viewerData.depthPrepass->UnregisterMaterial(*material); - if (const auto& pass = instancedRenderable->GetMaterial(i)->GetPass(m_forwardPassIndex)) - UnregisterMaterialPass(pass.get()); - } - - for (auto&& [viewer, viewerData] : m_viewers) - { - viewerData.rebuildDepthPrepass = true; - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; + viewerData.forwardPass->UnregisterMaterial(*material); + } } } void ForwardFramePipeline::UnregisterLight(Light* light) { m_lights.erase(light); + } - for (auto&& [viewer, viewerData] : m_viewers) - { - viewerData.rebuildForwardPass = true; - viewerData.prepare = true; - } + void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* materialPass) + { + auto it = m_activeMaterialPasses.find(materialPass); + assert(it != m_activeMaterialPasses.end()); + + MaterialPassData& materialData = it->second; + assert(materialData.usedCount > 0); + if (--materialData.usedCount == 0) + m_activeMaterialPasses.erase(materialPass); } void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance) @@ -676,65 +439,10 @@ namespace Nz { auto& viewerData = data; - FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass"); - depthPrepass.SetDepthStencilOutput(viewerData.depthStencilAttachment); - depthPrepass.SetDepthStencilClear(1.f, 0); - - depthPrepass.SetExecutionCallback([&]() - { - if (viewerData.rebuildDepthPrepass) - return FramePassExecution::UpdateAndExecute; - else - return FramePassExecution::Execute; - }); - - depthPrepass.SetCommandCallback([this, viewer = viewer, &viewerData](CommandBufferBuilder& builder, const Recti& /*renderRect*/) - { - Recti viewport = viewer->GetViewport(); + if (viewerData.depthPrepass) + viewerData.depthPrepass->RegisterToFrameGraph(frameGraph, viewerData.depthStencilAttachment); - builder.SetScissor(viewport); - builder.SetViewport(viewport); - - const auto& viewerInstance = viewer->GetViewerInstance(); - - ProcessRenderQueue(viewerData.depthPrepassRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) - { - ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; - elementRenderer.Render(viewerInstance, *viewerData.elementRendererData[elementType], builder, elementCount, elements); - }); - }); - - FramePass& forwardPass = frameGraph.AddPass("Forward pass"); - forwardPass.AddOutput(viewerData.colorAttachment); - forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment); - //forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment); - - forwardPass.SetClearColor(0, viewer->GetClearColor()); - forwardPass.SetDepthStencilClear(1.f, 0); - - forwardPass.SetExecutionCallback([&]() - { - if (viewerData.rebuildForwardPass) - return FramePassExecution::UpdateAndExecute; - else - return FramePassExecution::Execute; - }); - - forwardPass.SetCommandCallback([this, viewer = viewer, &viewerData](CommandBufferBuilder& builder, const Recti& /*renderRect*/) - { - Recti viewport = viewer->GetViewport(); - - builder.SetScissor(viewport); - builder.SetViewport(viewport); - - const auto& viewerInstance = viewer->GetViewerInstance(); - - ProcessRenderQueue(viewerData.forwardRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) - { - ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; - elementRenderer.Render(viewerInstance, *viewerData.elementRendererData[elementType], builder, elementCount, elements); - }); - }); + viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.colorAttachment, viewerData.depthStencilAttachment); } using ViewerPair = std::pair; @@ -808,60 +516,4 @@ namespace Nz return frameGraph.Bake(); } - - void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* material) - { - auto it = m_materials.find(material); - if (it == m_materials.end()) - { - it = m_materials.emplace(material, MaterialData{}).first; - it->second.onMaterialInvalided.Connect(material->OnMaterialInvalidated, [this, material](const MaterialPass* /*material*/) - { - m_invalidatedMaterials.insert(material); - }); - - m_invalidatedMaterials.insert(material); - } - - it->second.usedCount++; - } - - template - void ForwardFramePipeline::ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback) - { - if (renderQueue.empty()) - return; - - auto it = renderQueue.begin(); - auto itEnd = renderQueue.end(); - while (it != itEnd) - { - const RenderElement* element = *it; - UInt8 elementType = element->GetElementType(); - - const Pointer* first = it; - - ++it; - while (it != itEnd && (*it)->GetElementType() == elementType) - ++it; - - std::size_t count = it - first; - - if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType]) - continue; - - callback(elementType, first, count); - } - } - - void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* material) - { - auto it = m_materials.find(material); - assert(it != m_materials.end()); - - MaterialData& materialData = it->second; - assert(materialData.usedCount > 0); - if (--materialData.usedCount == 0) - m_materials.erase(material); - } } diff --git a/src/Nazara/Graphics/ForwardPipelinePass.cpp b/src/Nazara/Graphics/ForwardPipelinePass.cpp new file mode 100644 index 000000000..f8714accf --- /dev/null +++ b/src/Nazara/Graphics/ForwardPipelinePass.cpp @@ -0,0 +1,321 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + ForwardPipelinePass::ForwardPipelinePass(FramePipeline& owner, AbstractViewer* viewer) : + m_lastVisibilityHash(0), + m_viewer(viewer), + m_pipeline(owner), + m_rebuildCommandBuffer(false), + m_rebuildElements(false) + { + Graphics* graphics = Graphics::Instance(); + m_forwardPassIndex = graphics->GetMaterialPassRegistry().GetPassIndex("ForwardPass"); + m_lightUboPool = std::make_shared(); + } + + ForwardPipelinePass::~ForwardPipelinePass() + { + for (auto&& [materialPass, entry] : m_materialPasses) + m_pipeline.UnregisterMaterialPass(materialPass); + } + + void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash) + { + if (m_lastVisibilityHash != visibilityHash || m_rebuildElements) //< FIXME + { + renderFrame.PushForRelease(std::move(m_renderElements)); + m_renderElements.clear(); + m_renderQueueRegistry.Clear(); + m_renderQueue.Clear(); + m_lightBufferPerLights.clear(); + m_lightPerRenderElement.clear(); + + for (auto& lightDataUbo : m_lightDataBuffers) + { + renderFrame.PushReleaseCallback([pool = m_lightUboPool, lightUbo = std::move(lightDataUbo.renderBuffer)]() + { + pool->lightUboBuffers.push_back(std::move(lightUbo)); + }); + } + m_lightDataBuffers.clear(); + + Graphics* graphics = Graphics::Instance(); + + PredefinedLightData lightOffsets = PredefinedLightData::GetOffsets(); + std::size_t lightUboAlignedSize = AlignPow2(lightOffsets.totalSize, graphics->GetRenderDevice()->GetDeviceInfo().limits.minUniformBufferOffsetAlignment); + + UploadPool& uploadPool = renderFrame.GetUploadPool(); + + for (const auto& renderableData : visibleRenderables) + { + BoundingVolumef renderableBoundingVolume(renderableData.instancedRenderable->GetAABB()); + renderableBoundingVolume.Update(renderableData.worldInstance->GetWorldMatrix()); + + // Select lights + m_renderableLights.clear(); + for (const Light* light : visibleLights) + { + const BoundingVolumef& boundingVolume = light->GetBoundingVolume(); + if (boundingVolume.Intersect(renderableBoundingVolume.aabb)) + m_renderableLights.push_back(light); + } + + // Sort lights + std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const Light* lhs, const Light* rhs) + { + return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume); + }); + + std::size_t lightCount = std::min(m_renderableLights.size(), MaxLightCountPerDraw); + + LightKey lightKey; + lightKey.fill(nullptr); + for (std::size_t i = 0; i < lightCount; ++i) + lightKey[i] = m_renderableLights[i]; + + RenderBufferView lightUboView; + + auto it = m_lightBufferPerLights.find(lightKey); + if (it == m_lightBufferPerLights.end()) + { + // Prepare light ubo upload + + // Find light ubo + LightDataUbo* targetLightData = nullptr; + for (auto& lightUboData : m_lightDataBuffers) + { + if (lightUboData.offset + lightUboAlignedSize <= lightUboData.renderBuffer->GetSize()) + { + targetLightData = &lightUboData; + break; + } + } + + if (!targetLightData) + { + // Make a new light UBO + auto& lightUboData = m_lightDataBuffers.emplace_back(); + + // Reuse from pool if possible + if (!m_lightUboPool->lightUboBuffers.empty()) + { + lightUboData.renderBuffer = m_lightUboPool->lightUboBuffers.back(); + m_lightUboPool->lightUboBuffers.pop_back(); + } + else + lightUboData.renderBuffer = graphics->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, 256 * lightUboAlignedSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write); + + targetLightData = &lightUboData; + } + + assert(targetLightData); + if (!targetLightData->allocation) + targetLightData->allocation = &uploadPool.Allocate(targetLightData->renderBuffer->GetSize()); + + void* lightDataPtr = static_cast(targetLightData->allocation->mappedPtr) + targetLightData->offset; + AccessByOffset(lightDataPtr, lightOffsets.lightCountOffset) = SafeCast(lightCount); + + UInt8* lightPtr = static_cast(lightDataPtr) + lightOffsets.lightsOffset; + for (std::size_t i = 0; i < lightCount; ++i) + { + m_renderableLights[i]->FillLightData(lightPtr); + lightPtr += lightOffsets.lightSize; + } + + // Associate render element with light ubo + lightUboView = RenderBufferView(targetLightData->renderBuffer.get(), targetLightData->offset, lightUboAlignedSize); + + targetLightData->offset += lightUboAlignedSize; + + m_lightBufferPerLights.emplace(lightKey, lightUboView); + } + else + lightUboView = it->second; + + std::size_t previousCount = m_renderElements.size(); + renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, m_renderElements); + for (std::size_t i = previousCount; i < m_renderElements.size(); ++i) + { + const RenderElement* element = m_renderElements[i].get(); + m_lightPerRenderElement.emplace(element, lightUboView); + } + } + + for (const auto& renderElement : m_renderElements) + { + renderElement->Register(m_renderQueueRegistry); + m_renderQueue.Insert(renderElement.get()); + } + + m_renderQueueRegistry.Finalize(); + + renderFrame.Execute([&](CommandBufferBuilder& builder) + { + builder.BeginDebugRegion("Light UBO Update", Color::Yellow); + { + for (auto& lightUboData : m_lightDataBuffers) + { + if (!lightUboData.allocation) + continue; + + builder.CopyBuffer(*lightUboData.allocation, RenderBufferView(lightUboData.renderBuffer.get(), 0, lightUboData.offset)); + } + + builder.PostTransferBarrier(); + } + builder.EndDebugRegion(); + }, QueueType::Transfer); + + m_lastVisibilityHash = visibilityHash; + m_rebuildElements = true; + } + + // TODO: Don't sort every frame if no material pass requires distance sorting + m_renderQueue.Sort([&](const RenderElement* element) + { + return element->ComputeSortingScore(frustum, m_renderQueueRegistry); + }); + + if (m_rebuildElements) + { + m_pipeline.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) + { + if (elementType >= m_elementRendererData.size() || !m_elementRendererData[elementType]) + { + if (elementType >= m_elementRendererData.size()) + m_elementRendererData.resize(elementType + 1); + + m_elementRendererData[elementType] = elementRenderer.InstanciateData(); + } + + elementRenderer.Reset(*m_elementRendererData[elementType], renderFrame); + }); + + const auto& viewerInstance = m_viewer->GetViewerInstance(); + + auto& lightPerRenderElement = m_lightPerRenderElement; + m_pipeline.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = m_pipeline.GetElementRenderer(elementType); + + m_renderStates.clear(); + + m_renderStates.reserve(elementCount); + for (std::size_t i = 0; i < elementCount; ++i) + { + auto it = lightPerRenderElement.find(elements[i]); + assert(it != lightPerRenderElement.end()); + + auto& renderStates = m_renderStates.emplace_back(); + renderStates.lightData = it->second; + } + + elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data()); + }); + + m_pipeline.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) + { + elementRenderer.PrepareEnd(renderFrame, *m_elementRendererData[elementType]); + }); + + m_rebuildCommandBuffer = true; + m_rebuildElements = false; + } + } + + void ForwardPipelinePass::RegisterMaterial(const Material& material) + { + if (!material.HasPass(m_forwardPassIndex)) + return; + + MaterialPass* materialPass = material.GetPass(m_forwardPassIndex).get(); + + auto it = m_materialPasses.find(materialPass); + if (it == m_materialPasses.end()) + { + m_pipeline.RegisterMaterialPass(materialPass); + + auto& matPassEntry = m_materialPasses[materialPass]; + matPassEntry.onMaterialPipelineInvalidated.Connect(materialPass->OnMaterialPassPipelineInvalidated, [=](const MaterialPass*) + { + m_rebuildElements = true; + }); + + matPassEntry.onMaterialShaderBindingInvalidated.Connect(materialPass->OnMaterialPassShaderBindingInvalidated, [=](const MaterialPass*) + { + m_rebuildCommandBuffer = true; + }); + } + else + it->second.usedCount++; + } + + void ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex) + { + FramePass& forwardPass = frameGraph.AddPass("Forward pass"); + forwardPass.AddOutput(colorBufferIndex); + forwardPass.SetDepthStencilInput(depthBufferIndex); + + forwardPass.SetClearColor(0, m_viewer->GetClearColor()); + forwardPass.SetDepthStencilClear(1.f, 0); + + forwardPass.SetExecutionCallback([&]() + { + if (m_rebuildCommandBuffer) + return FramePassExecution::UpdateAndExecute; + else + return FramePassExecution::Execute; + }); + + forwardPass.SetCommandCallback([this](CommandBufferBuilder& builder, const Recti& /*renderRect*/) + { + Recti viewport = m_viewer->GetViewport(); + + builder.SetScissor(viewport); + builder.SetViewport(viewport); + + const auto& viewerInstance = m_viewer->GetViewerInstance(); + + m_pipeline.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = m_pipeline.GetElementRenderer(elementType); + elementRenderer.Render(viewerInstance, *m_elementRendererData[elementType], builder, elementCount, elements); + }); + + m_rebuildCommandBuffer = false; + }); + } + + void ForwardPipelinePass::UnregisterMaterial(const Material& material) + { + if (!material.HasPass(m_forwardPassIndex)) + return; + + MaterialPass* materialPass = material.GetPass(m_forwardPassIndex).get(); + + auto it = m_materialPasses.find(materialPass); + if (it != m_materialPasses.end()) + { + if (--it->second.usedCount == 0) + { + m_materialPasses.erase(it); + m_pipeline.UnregisterMaterialPass(materialPass); + } + } + } +} diff --git a/src/Nazara/Graphics/FramePipeline.cpp b/src/Nazara/Graphics/FramePipeline.cpp index ea01267d1..360cdb2d7 100644 --- a/src/Nazara/Graphics/FramePipeline.cpp +++ b/src/Nazara/Graphics/FramePipeline.cpp @@ -3,9 +3,20 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include +#include +#include #include namespace Nz { + FramePipeline::FramePipeline() + { + m_elementRenderers.resize(BasicRenderElementCount); + m_elementRenderers[UnderlyingCast(BasicRenderElement::SpriteChain)] = std::make_unique(*Graphics::Instance()->GetRenderDevice()); + m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique(); + } + FramePipeline::~FramePipeline() = default; } diff --git a/src/Nazara/Graphics/FramePipelinePass.cpp b/src/Nazara/Graphics/FramePipelinePass.cpp new file mode 100644 index 000000000..451a0678c --- /dev/null +++ b/src/Nazara/Graphics/FramePipelinePass.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + FramePipelinePass::~FramePipelinePass() = default; +} diff --git a/src/Nazara/Graphics/Material.cpp b/src/Nazara/Graphics/Material.cpp index d55a5db12..2ba96b8ca 100644 --- a/src/Nazara/Graphics/Material.cpp +++ b/src/Nazara/Graphics/Material.cpp @@ -7,7 +7,4 @@ namespace Nz { - Material::Material() - { - } } diff --git a/src/Nazara/Graphics/MaterialPass.cpp b/src/Nazara/Graphics/MaterialPass.cpp index 78fb86631..1cf060b7c 100644 --- a/src/Nazara/Graphics/MaterialPass.cpp +++ b/src/Nazara/Graphics/MaterialPass.cpp @@ -28,7 +28,6 @@ namespace Nz */ MaterialPass::MaterialPass(std::shared_ptr settings) : m_settings(std::move(settings)), - m_forceCommandBufferRegeneration(false), m_pipelineUpdated(false) { m_pipelineInfo.settings = m_settings; @@ -109,7 +108,7 @@ namespace Nz } } - bool MaterialPass::Update(RenderFrame& renderFrame, CommandBufferBuilder& builder) + void MaterialPass::Update(RenderFrame& renderFrame, CommandBufferBuilder& builder) { UploadPool& uploadPool = renderFrame.GetUploadPool(); @@ -125,11 +124,6 @@ namespace Nz ubo.dataInvalidated = false; } } - - bool shouldRegenerateCommandBuffer = m_forceCommandBufferRegeneration; - m_forceCommandBufferRegeneration = false; - - return shouldRegenerateCommandBuffer; } void MaterialPass::UpdatePipeline() const