From 8546631f626f81db37dd278ed70b41f36fe9f21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Thu, 19 Aug 2021 23:26:34 +0200 Subject: [PATCH] Add frustum culling (WIP) --- examples/DeferredShading/main.cpp | 4 +- examples/GraphicsTest/main.cpp | 2 +- examples/PhysicsDemo/main.cpp | 11 +- .../Nazara/Graphics/ForwardFramePipeline.hpp | 26 +- .../Nazara/Graphics/InstancedRenderable.hpp | 7 +- .../Nazara/Graphics/InstancedRenderable.inl | 9 + include/Nazara/Graphics/Model.hpp | 2 +- include/Nazara/Graphics/ViewerInstance.hpp | 6 + include/Nazara/Graphics/ViewerInstance.inl | 30 + include/Nazara/Graphics/WorldInstance.hpp | 2 + include/Nazara/Graphics/WorldInstance.inl | 10 + include/Nazara/Math/Frustum.inl | 36 +- include/Nazara/Math/OrientedBox.inl | 4 +- src/Nazara/Graphics/ForwardFramePipeline.cpp | 954 ++++++++++-------- src/Nazara/Graphics/Model.cpp | 3 +- 15 files changed, 643 insertions(+), 463 deletions(-) diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 0332b9e22..325266485 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -211,11 +211,11 @@ int main() } planeMat->AddPass("ForwardPass", planeMatPass); - Nz::Model spaceshipModel(std::move(gfxMesh)); + Nz::Model spaceshipModel(std::move(gfxMesh), spaceship->GetAABB()); for (std::size_t i = 0; i < spaceshipModel.GetSubMeshCount(); ++i) spaceshipModel.SetMaterial(i, spaceshipMat); - Nz::Model planeModel(std::move(planeMeshGfx)); + Nz::Model planeModel(std::move(planeMeshGfx), planeMesh->GetAABB()); for (std::size_t i = 0; i < planeModel.GetSubMeshCount(); ++i) planeModel.SetMaterial(i, planeMat); diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index 0abb250b4..c8ce6c52f 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -75,7 +75,7 @@ int main() basicMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams)); basicMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png", texParams)); - Nz::Model model(std::move(gfxMesh)); + Nz::Model model(std::move(gfxMesh), spaceshipMesh->GetAABB()); for (std::size_t i = 0; i < model.GetSubMeshCount(); ++i) model.SetMaterial(i, material); diff --git a/examples/PhysicsDemo/main.cpp b/examples/PhysicsDemo/main.cpp index 0fee85707..5bf63907a 100644 --- a/examples/PhysicsDemo/main.cpp +++ b/examples/PhysicsDemo/main.cpp @@ -61,6 +61,7 @@ int main() return __LINE__; } + const Nz::Boxf& spaceshipAABB = spaceshipMesh->GetAABB(); std::shared_ptr gfxMesh = std::make_shared(*spaceshipMesh); // Texture @@ -102,7 +103,7 @@ int main() Nz::DepthMaterial basicMatDepth(*depthPass); basicMatDepth.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams)); - std::shared_ptr model = std::make_shared(std::move(gfxMesh)); + std::shared_ptr model = std::make_shared(std::move(gfxMesh), spaceshipAABB); for (std::size_t i = 0; i < model->GetSubMeshCount(); ++i) model->SetMaterial(i, material); @@ -140,7 +141,7 @@ int main() std::shared_ptr colliderMesh = Nz::Mesh::Build(shipCollider->GenerateMesh()); std::shared_ptr colliderGraphicalMesh = std::make_shared(*colliderMesh); - colliderModel = std::make_shared(colliderGraphicalMesh); + colliderModel = std::make_shared(colliderGraphicalMesh, spaceshipAABB); for (std::size_t i = 0; i < colliderModel->GetSubMeshCount(); ++i) colliderModel->SetMaterial(i, colliderMat); } @@ -168,11 +169,11 @@ int main() registry.get(viewer).SetParent(registry, headingEntity); registry.get(viewer).SetPosition(Nz::Vector3f::Backward() * 2.5f + Nz::Vector3f::Up() * 1.f); - for (std::size_t x = 0; x < 5; ++x) + for (std::size_t x = 0; x < 10; ++x) { - for (std::size_t y = 0; y < 5; ++y) + for (std::size_t y = 0; y < 10; ++y) { - for (std::size_t z = 0; z < 5; ++z) + for (std::size_t z = 0; z < 10; ++z) { entt::entity entity = registry.create(); auto& entityGfx = registry.emplace(entity); diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index f01e94002..1325169fc 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -54,6 +54,12 @@ namespace Nz void ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue& renderQueue); void UnregisterMaterialPass(MaterialPass* material); + struct ElementAABB + { + Boxf aabb; + std::size_t count; + }; + struct MaterialData { std::size_t usedCount = 0; @@ -70,7 +76,19 @@ namespace Nz { std::size_t colorAttachment; std::size_t depthStencilAttachment; + std::size_t depthPrepassVisibilityHash = 0; + std::size_t forwardVisibilityHash = 0; + std::vector depthPrepassAABB; + std::vector forwardAABB; + std::vector> depthPrepassRenderElements; + std::vector> forwardRenderElements; + RenderQueueRegistry depthPrepassRegistry; + RenderQueueRegistry forwardRegistry; + RenderQueue depthPrepassRenderQueue; + RenderQueue forwardRenderQueue; ShaderBindingPtr blitShaderBinding; + bool rebuildDepthPrepass = true; + bool rebuildForwardPass = true; }; std::size_t m_depthPassIndex; @@ -82,17 +100,9 @@ namespace Nz std::unordered_set m_invalidatedMaterials; std::unordered_set m_invalidatedWorldInstances; std::unordered_set m_removedWorldInstances; - std::vector> m_depthPrepassRenderElements; - std::vector> m_forwardRenderElements; std::vector> m_elementRenderers; BakedFrameGraph m_bakedFrameGraph; - RenderQueueRegistry m_depthPrepassRegistry; - RenderQueueRegistry m_forwardRegistry; - RenderQueue m_depthPrepassRenderQueue; - RenderQueue m_forwardRenderQueue; - bool m_rebuildDepthPrepass; bool m_rebuildFrameGraph; - bool m_rebuildForwardPass; }; } diff --git a/include/Nazara/Graphics/InstancedRenderable.hpp b/include/Nazara/Graphics/InstancedRenderable.hpp index 0c48d89ec..7189d847a 100644 --- a/include/Nazara/Graphics/InstancedRenderable.hpp +++ b/include/Nazara/Graphics/InstancedRenderable.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Nz @@ -22,13 +23,14 @@ namespace Nz class NAZARA_GRAPHICS_API InstancedRenderable { public: - InstancedRenderable() = default; + inline InstancedRenderable(const Boxf& aabb); InstancedRenderable(const InstancedRenderable&) = delete; InstancedRenderable(InstancedRenderable&&) noexcept = default; ~InstancedRenderable(); virtual void BuildElement(std::size_t passIndex, WorldInstance& worldInstance, std::vector>& elements) const = 0; + inline const Boxf& GetAABB() const; virtual const std::shared_ptr& GetMaterial(std::size_t i) const = 0; virtual std::size_t GetMaterialCount() const = 0; @@ -36,6 +38,9 @@ namespace Nz InstancedRenderable& operator=(InstancedRenderable&&) noexcept = default; NazaraSignal(OnMaterialInvalidated, InstancedRenderable* /*instancedRenderable*/, std::size_t /*materialIndex*/, const std::shared_ptr& /*newMaterial*/); + + private: + Boxf m_aabb; }; } diff --git a/include/Nazara/Graphics/InstancedRenderable.inl b/include/Nazara/Graphics/InstancedRenderable.inl index f5e049f05..44e42fde5 100644 --- a/include/Nazara/Graphics/InstancedRenderable.inl +++ b/include/Nazara/Graphics/InstancedRenderable.inl @@ -7,6 +7,15 @@ namespace Nz { + inline InstancedRenderable::InstancedRenderable(const Boxf& aabb) : + m_aabb(aabb) + { + } + + inline const Boxf& InstancedRenderable::GetAABB() const + { + return m_aabb; + } } #include diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index bfda5eff6..dde892dd5 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -23,7 +23,7 @@ namespace Nz class NAZARA_GRAPHICS_API Model : public InstancedRenderable { public: - Model(std::shared_ptr graphicalMesh); + Model(std::shared_ptr graphicalMesh, const Boxf& aabb); Model(const Model&) = delete; Model(Model&&) noexcept = default; ~Model() = default; diff --git a/include/Nazara/Graphics/ViewerInstance.hpp b/include/Nazara/Graphics/ViewerInstance.hpp index bc7ddd0a9..91e42a23a 100644 --- a/include/Nazara/Graphics/ViewerInstance.hpp +++ b/include/Nazara/Graphics/ViewerInstance.hpp @@ -28,6 +28,12 @@ namespace Nz ViewerInstance(ViewerInstance&&) noexcept = default; ~ViewerInstance() = default; + inline const Matrix4f& GetInvProjectionMatrix() const; + inline const Matrix4f& GetInvViewMatrix() const; + inline const Matrix4f& GetInvViewProjMatrix() const; + inline const Matrix4f& GetProjectionMatrix() const; + inline const Matrix4f& GetViewMatrix() const; + inline const Matrix4f& GetViewProjMatrix() const; inline std::shared_ptr& GetInstanceBuffer(); inline const std::shared_ptr& GetInstanceBuffer() const; inline ShaderBinding& GetShaderBinding(); diff --git a/include/Nazara/Graphics/ViewerInstance.inl b/include/Nazara/Graphics/ViewerInstance.inl index 89b200687..bf269cd42 100644 --- a/include/Nazara/Graphics/ViewerInstance.inl +++ b/include/Nazara/Graphics/ViewerInstance.inl @@ -8,6 +8,36 @@ namespace Nz { + inline const Matrix4f& ViewerInstance::GetInvProjectionMatrix() const + { + return m_invProjectionMatrix; + } + + inline const Matrix4f& ViewerInstance::GetInvViewMatrix() const + { + return m_invViewMatrix; + } + + inline const Matrix4f& ViewerInstance::GetInvViewProjMatrix() const + { + return m_invViewProjMatrix; + } + + inline const Matrix4f& ViewerInstance::GetProjectionMatrix() const + { + return m_projectionMatrix; + } + + inline const Matrix4f& ViewerInstance::GetViewMatrix() const + { + return m_viewMatrix; + } + + inline const Matrix4f& ViewerInstance::GetViewProjMatrix() const + { + return m_viewProjMatrix; + } + inline std::shared_ptr& ViewerInstance::GetInstanceBuffer() { return m_viewerDataBuffer; diff --git a/include/Nazara/Graphics/WorldInstance.hpp b/include/Nazara/Graphics/WorldInstance.hpp index acae986c5..a402a6169 100644 --- a/include/Nazara/Graphics/WorldInstance.hpp +++ b/include/Nazara/Graphics/WorldInstance.hpp @@ -33,8 +33,10 @@ namespace Nz inline std::shared_ptr& GetInstanceBuffer(); inline const std::shared_ptr& GetInstanceBuffer() const; + inline const Matrix4f& GetInvWorldMatrix() const; inline ShaderBinding& GetShaderBinding(); inline const ShaderBinding& GetShaderBinding() const; + inline const Matrix4f& GetWorldMatrix() const; void UpdateBuffers(UploadPool& uploadPool, CommandBufferBuilder& builder); inline void UpdateWorldMatrix(const Matrix4f& worldMatrix); diff --git a/include/Nazara/Graphics/WorldInstance.inl b/include/Nazara/Graphics/WorldInstance.inl index 4bca9a2dd..c5537bc5c 100644 --- a/include/Nazara/Graphics/WorldInstance.inl +++ b/include/Nazara/Graphics/WorldInstance.inl @@ -18,6 +18,11 @@ namespace Nz return m_instanceDataBuffer; } + inline const Matrix4f& WorldInstance::GetInvWorldMatrix() const + { + return m_invWorldMatrix; + } + inline ShaderBinding& WorldInstance::GetShaderBinding() { return *m_shaderBinding; @@ -28,6 +33,11 @@ namespace Nz return *m_shaderBinding; } + inline const Matrix4f& WorldInstance::GetWorldMatrix() const + { + return m_worldMatrix; + } + inline void WorldInstance::UpdateWorldMatrix(const Matrix4f& worldMatrix) { m_worldMatrix = worldMatrix; diff --git a/include/Nazara/Math/Frustum.inl b/include/Nazara/Math/Frustum.inl index ee060277e..59b0bfbe9 100644 --- a/include/Nazara/Math/Frustum.inl +++ b/include/Nazara/Math/Frustum.inl @@ -264,7 +264,7 @@ namespace Nz plane[2] *= invLength; plane[3] *= -invLength; - m_planes[FrustumPlane::Right].Set(plane); + m_planes[UnderlyingCast(FrustumPlane::Right)].Set(plane); // Extract the numbers for the LEFT plane plane[0] = clipMatrix[ 3] + clipMatrix[ 0]; @@ -279,7 +279,7 @@ namespace Nz plane[2] *= invLength; plane[3] *= -invLength; - m_planes[FrustumPlane::Left].Set(plane); + m_planes[UnderlyingCast(FrustumPlane::Left)].Set(plane); // Extract the BOTTOM plane plane[0] = clipMatrix[ 3] + clipMatrix[ 1]; @@ -294,7 +294,7 @@ namespace Nz plane[2] *= invLength; plane[3] *= -invLength; - m_planes[FrustumPlane::Bottom].Set(plane); + m_planes[UnderlyingCast(FrustumPlane::Bottom)].Set(plane); // Extract the TOP plane plane[0] = clipMatrix[ 3] - clipMatrix[ 1]; @@ -309,7 +309,7 @@ namespace Nz plane[2] *= invLength; plane[3] *= -invLength; - m_planes[FrustumPlane::Top].Set(plane); + m_planes[UnderlyingCast(FrustumPlane::Top)].Set(plane); // Extract the FAR plane plane[0] = clipMatrix[ 3] - clipMatrix[ 2]; @@ -324,7 +324,7 @@ namespace Nz plane[2] *= invLength; plane[3] *= -invLength; - m_planes[FrustumPlane::Far].Set(plane); + m_planes[UnderlyingCast(FrustumPlane::Far)].Set(plane); // Extract the NEAR plane plane[0] = clipMatrix[ 3] + clipMatrix[ 2]; @@ -339,7 +339,7 @@ namespace Nz plane[2] *= invLength; plane[3] *= -invLength; - m_planes[FrustumPlane::Near].Set(plane); + m_planes[UnderlyingCast(FrustumPlane::Near)].Set(plane); // Once planes have been extracted, we must extract points of the frustum // Based on: http://www.gamedev.net/topic/393309-calculating-the-view-frustums-vertices/ @@ -354,56 +354,56 @@ namespace Nz corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::FarLeftBottom] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::FarLeftBottom)] = Vector3(corner.x, corner.y, corner.z); // FarLeftTop corner.Set(T(-1.0), T(1.0), T(1.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::FarLeftTop] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::FarLeftTop)] = Vector3(corner.x, corner.y, corner.z); // FarRightBottom corner.Set(T(1.0), T(-1.0), T(1.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::FarRightBottom] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::FarRightBottom)] = Vector3(corner.x, corner.y, corner.z); // FarRightTop corner.Set(T(1.0), T(1.0), T(1.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::FarRightTop] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::FarRightTop)] = Vector3(corner.x, corner.y, corner.z); // NearLeftBottom corner.Set(T(-1.0), T(-1.0), T(0.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::NearLeftBottom] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::NearLeftBottom)] = Vector3(corner.x, corner.y, corner.z); // NearLeftTop corner.Set(T(-1.0), T(1.0), T(0.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::NearLeftTop] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::NearLeftTop)] = Vector3(corner.x, corner.y, corner.z); // NearRightBottom corner.Set(T(1.0), T(-1.0), T(0.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::NearRightBottom] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::NearRightBottom)] = Vector3(corner.x, corner.y, corner.z); // NearRightTop corner.Set(T(1.0), T(1.0), T(0.0)); corner = invClipMatrix.Transform(corner); corner.Normalize(); - m_corners[BoxCorner::NearRightTop] = Vector3(corner.x, corner.y, corner.z); + m_corners[UnderlyingCast(BoxCorner::NearRightTop)] = Vector3(corner.x, corner.y, corner.z); } else NazaraWarning("Clip matrix is not invertible, failed to compute frustum corners"); @@ -440,9 +440,9 @@ namespace Nz const Vector3& Frustum::GetCorner(BoxCorner corner) const { #ifdef NAZARA_DEBUG - if (corner > BoxCornerCount) + if (UnderlyingCast(corner) > BoxCornerCount) { - NazaraError("Corner not handled (0x" + NumberToString(corner, 16) + ')'); + NazaraError("Corner not handled (0x" + NumberToString(UnderlyingCast(corner), 16) + ')'); static Vector3 dummy; return dummy; @@ -465,9 +465,9 @@ namespace Nz const Plane& Frustum::GetPlane(FrustumPlane plane) const { #ifdef NAZARA_DEBUG - if (plane > FrustumPlane::Max) + if (UnderlyingCast(plane) > FrustumPlaneCount) { - NazaraError("Frustum plane not handled (0x" + NumberToString(plane, 16) + ')'); + NazaraError("Frustum plane not handled (0x" + NumberToString(UnderlyingCast(plane), 16) + ')'); static Plane dummy; return dummy; diff --git a/include/Nazara/Math/OrientedBox.inl b/include/Nazara/Math/OrientedBox.inl index ca6400fe2..c03e126b5 100644 --- a/include/Nazara/Math/OrientedBox.inl +++ b/include/Nazara/Math/OrientedBox.inl @@ -89,9 +89,9 @@ namespace Nz const Vector3& OrientedBox::GetCorner(BoxCorner corner) const { #ifdef NAZARA_DEBUG - if (corner > BoxCornerCount) + if (UnderlyingCast(corner) > BoxCornerCount) { - NazaraError("Corner not handled (0x" + NumberToString(corner, 16) + ')'); + NazaraError("Corner not handled (0x" + NumberToString(UnderlyingCast(corner), 16) + ')'); static Vector3 dummy; return dummy; diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index fae400902..5b4b9c112 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -1,442 +1,548 @@ -// Copyright (C) 2017 Jérôme Leclercq -// This file is part of the "Nazara Engine - Graphics module" -// For conditions of distribution and use, see copyright notice in Config.hpp +// Copyright (C) 2017 Jérôme Leclercq +// 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 +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + ForwardFramePipeline::ForwardFramePipeline() : + m_rebuildFrameGraph(true) + { + auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry(); + m_depthPassIndex = passRegistry.GetPassIndex("DepthPass"); + m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass"); + + m_elementRenderers.resize(1); + m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique(); + } + + void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance) + { + m_invalidatedViewerInstances.insert(viewerInstance); + } + + void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance) + { + m_invalidatedWorldInstances.insert(worldInstance); + } + + void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable) + { + m_removedWorldInstances.erase(worldInstance); + + auto& renderableMap = m_renderables[worldInstance]; + if (renderableMap.empty()) + InvalidateWorldInstance(worldInstance.get()); + + if (auto it = renderableMap.find(instancedRenderable); it == renderableMap.end()) + { + auto& renderableData = renderableMap.emplace(instancedRenderable, RenderableData{}).first->second; + renderableData.onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr& newMaterial) + { + if (newMaterial) + { + if (MaterialPass* pass = newMaterial->GetPass(m_depthPassIndex)) + RegisterMaterialPass(pass); + + if (MaterialPass* pass = newMaterial->GetPass(m_forwardPassIndex)) + RegisterMaterialPass(pass); + } + + const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex); + if (prevMaterial) + { + if (MaterialPass* pass = prevMaterial->GetPass(m_depthPassIndex)) + UnregisterMaterialPass(pass); + + if (MaterialPass* pass = prevMaterial->GetPass(m_forwardPassIndex)) + UnregisterMaterialPass(pass); + } -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + for (auto&& [viewer, viewerData] : m_viewers) + { + viewerData.rebuildDepthPrepass = true; + viewerData.rebuildForwardPass = true; + } + }); + + std::size_t matCount = instancedRenderable->GetMaterialCount(); + for (std::size_t i = 0; i < matCount; ++i) + { + if (Material* mat = instancedRenderable->GetMaterial(i).get()) + { + if (MaterialPass* pass = mat->GetPass(m_depthPassIndex)) + RegisterMaterialPass(pass); + + if (MaterialPass* pass = mat->GetPass(m_forwardPassIndex)) + RegisterMaterialPass(pass); + } + } -namespace Nz -{ - ForwardFramePipeline::ForwardFramePipeline() : - m_rebuildFrameGraph(true), - m_rebuildDepthPrepass(false), - m_rebuildForwardPass(false) - { - auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry(); - m_depthPassIndex = passRegistry.GetPassIndex("DepthPass"); - m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass"); - - m_elementRenderers.resize(1); - m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique(); - } - - void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance) - { + for (auto&& [viewer, viewerData] : m_viewers) + { + viewerData.rebuildDepthPrepass = true; + viewerData.rebuildForwardPass = true; + } + } + } + + void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance) + { + m_viewers.emplace(viewerInstance, ViewerData{}); m_invalidatedViewerInstances.insert(viewerInstance); - } - - void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance) - { - m_invalidatedWorldInstances.insert(worldInstance); - } - - void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable) - { - m_removedWorldInstances.erase(worldInstance); - - auto& renderableMap = m_renderables[worldInstance]; - if (renderableMap.empty()) - InvalidateWorldInstance(worldInstance.get()); - - if (auto it = renderableMap.find(instancedRenderable); it == renderableMap.end()) - { - auto& renderableData = renderableMap.emplace(instancedRenderable, RenderableData{}).first->second; - renderableData.onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr& newMaterial) - { - if (newMaterial) - { - if (MaterialPass* pass = newMaterial->GetPass(m_depthPassIndex)) - RegisterMaterialPass(pass); - - if (MaterialPass* pass = newMaterial->GetPass(m_forwardPassIndex)) - RegisterMaterialPass(pass); - } - - const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex); - if (prevMaterial) - { - if (MaterialPass* pass = prevMaterial->GetPass(m_depthPassIndex)) - UnregisterMaterialPass(pass); - - if (MaterialPass* pass = prevMaterial->GetPass(m_forwardPassIndex)) - UnregisterMaterialPass(pass); - } - - m_rebuildDepthPrepass = true; - m_rebuildForwardPass = true; - }); - - std::size_t matCount = instancedRenderable->GetMaterialCount(); - for (std::size_t i = 0; i < matCount; ++i) - { - if (Material* mat = instancedRenderable->GetMaterial(i).get()) - { - if (MaterialPass* pass = mat->GetPass(m_depthPassIndex)) - RegisterMaterialPass(pass); - - if (MaterialPass* pass = mat->GetPass(m_forwardPassIndex)) - RegisterMaterialPass(pass); - } - } - - m_rebuildDepthPrepass = true; - m_rebuildForwardPass = true; - } - } - - void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance) - { - m_viewers.emplace(viewerInstance, ViewerData{}); - m_invalidatedViewerInstances.insert(viewerInstance); - } - - void ForwardFramePipeline::Render(RenderFrame& renderFrame) - { - Graphics* graphics = Graphics::Instance(); - - renderFrame.PushForRelease(std::move(m_removedWorldInstances)); - m_removedWorldInstances.clear(); - - if (m_rebuildFrameGraph) - { - renderFrame.PushForRelease(std::move(m_bakedFrameGraph)); - m_bakedFrameGraph = BuildFrameGraph(); - } - - // Update UBOs and materials - UploadPool& uploadPool = renderFrame.GetUploadPool(); - - renderFrame.Execute([&](CommandBufferBuilder& builder) - { - builder.BeginDebugRegion("UBO Update", Color::Yellow); - { - builder.PreTransferBarrier(); - - for (AbstractViewer* viewer : m_invalidatedViewerInstances) - viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder); - - m_invalidatedViewerInstances.clear(); - - for (WorldInstance* worldInstance : m_invalidatedWorldInstances) - worldInstance->UpdateBuffers(uploadPool, builder); - - m_invalidatedWorldInstances.clear(); - - for (MaterialPass* material : m_invalidatedMaterials) - { - if (material->Update(renderFrame, builder)) + m_rebuildFrameGraph = true; + } + + void ForwardFramePipeline::Render(RenderFrame& renderFrame) + { + Graphics* graphics = Graphics::Instance(); + + renderFrame.PushForRelease(std::move(m_removedWorldInstances)); + m_removedWorldInstances.clear(); + + if (m_rebuildFrameGraph) + { + renderFrame.PushForRelease(std::move(m_bakedFrameGraph)); + m_bakedFrameGraph = BuildFrameGraph(); + } + + // Update UBOs and materials + UploadPool& uploadPool = renderFrame.GetUploadPool(); + + renderFrame.Execute([&](CommandBufferBuilder& builder) + { + builder.BeginDebugRegion("UBO Update", Color::Yellow); + { + builder.PreTransferBarrier(); + + for (AbstractViewer* viewer : m_invalidatedViewerInstances) + viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder); + + m_invalidatedViewerInstances.clear(); + + for (WorldInstance* worldInstance : m_invalidatedWorldInstances) + worldInstance->UpdateBuffers(uploadPool, builder); + + m_invalidatedWorldInstances.clear(); + + for (MaterialPass* material : m_invalidatedMaterials) + { + if (material->Update(renderFrame, builder)) { - m_rebuildDepthPrepass = true; - m_rebuildForwardPass = true; - } - } - m_invalidatedMaterials.clear(); + for (auto&& [viewer, viewerData] : m_viewers) + { + viewerData.rebuildDepthPrepass = true; + viewerData.rebuildForwardPass = true; + } + } + } + m_invalidatedMaterials.clear(); + + builder.PostTransferBarrier(); + } + builder.EndDebugRegion(); + }, QueueType::Transfer); - builder.PostTransferBarrier(); - } - builder.EndDebugRegion(); - }, QueueType::Transfer); - - if (m_rebuildDepthPrepass) + auto CombineHash = [](std::size_t currentHash, std::size_t newHash) { - m_depthPrepassRenderElements.clear(); - - for (const auto& [worldInstance, renderables] : m_renderables) - { - for (const auto& [renderable, renderableData] : renderables) - renderable->BuildElement(m_depthPassIndex, *worldInstance, m_depthPrepassRenderElements); - } - } - - if (m_rebuildForwardPass) + return currentHash * 23 + newHash; + }; + + // Render queues handling + for (auto&& [viewer, data] : m_viewers) { - m_forwardRenderElements.clear(); - - for (const auto& [worldInstance, renderables] : m_renderables) - { - for (const auto& [renderable, renderableData] : renderables) - renderable->BuildElement(m_forwardPassIndex, *worldInstance, m_forwardRenderElements); - } - } - - // RenderQueue handling - m_depthPrepassRegistry.Clear(); - m_depthPrepassRenderQueue.Clear(); - for (const auto& renderElement : m_depthPrepassRenderElements) - { - renderElement->Register(m_depthPrepassRegistry); - m_depthPrepassRenderQueue.Insert(renderElement.get()); - } - - m_depthPrepassRenderQueue.Sort([&](const RenderElement* element) - { - return element->ComputeSortingScore(m_depthPrepassRegistry); - }); - - m_forwardRegistry.Clear(); - m_forwardRenderQueue.Clear(); - for (const auto& renderElement : m_forwardRenderElements) - { - renderElement->Register(m_forwardRegistry); - m_forwardRenderQueue.Insert(renderElement.get()); - } - - m_forwardRenderQueue.Sort([&](const RenderElement* element) - { - return element->ComputeSortingScore(m_forwardRegistry); - }); - - if (m_bakedFrameGraph.Resize(renderFrame)) - { - const std::shared_ptr& sampler = graphics->GetSamplerCache().Get({}); - for (auto&& [_, viewerData] : m_viewers) - { - if (viewerData.blitShaderBinding) - renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding)); - - viewerData.blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0); - viewerData.blitShaderBinding->Update({ + auto& viewerData = data; + + //if (viewerData.rebuildDepthPrepass) + { + viewerData.depthPrepassAABB.clear(); + viewerData.depthPrepassRenderElements.clear(); + + for (const auto& [worldInstance, renderables] : m_renderables) + { + for (const auto& [renderable, renderableData] : renderables) { - 0, - ShaderBinding::TextureBinding { - m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment).get(), - sampler.get() - } + std::size_t prevCount = viewerData.depthPrepassRenderElements.size(); + renderable->BuildElement(m_depthPassIndex, *worldInstance, viewerData.depthPrepassRenderElements); + std::size_t currentCount = viewerData.depthPrepassRenderElements.size(); + + if (currentCount > prevCount) + { + BoundingVolumef boundingVolume(renderable->GetAABB()); + boundingVolume.Update(worldInstance->GetWorldMatrix()); + + auto& aabbData = viewerData.depthPrepassAABB.emplace_back(); + aabbData.aabb = boundingVolume.aabb; + aabbData.count = currentCount - prevCount; + } + } + } + } + + //if (viewerData.rebuildForwardPass) + { + viewerData.forwardAABB.clear(); + viewerData.forwardRenderElements.clear(); + + for (const auto& [worldInstance, renderables] : m_renderables) + { + for (const auto& [renderable, renderableData] : renderables) + { + std::size_t prevCount = viewerData.forwardRenderElements.size(); + renderable->BuildElement(m_forwardPassIndex, *worldInstance, viewerData.forwardRenderElements); + std::size_t currentCount = viewerData.forwardRenderElements.size(); + + if (currentCount > prevCount) + { + BoundingVolumef boundingVolume(renderable->GetAABB()); + boundingVolume.Update(worldInstance->GetWorldMatrix()); + + auto& aabbData = viewerData.forwardAABB.emplace_back(); + aabbData.aabb = boundingVolume.aabb; + aabbData.count = currentCount - prevCount; + } + } + } + } + + const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix(); + + Frustumf frustum; + frustum.Extract(viewProjMatrix); + + viewerData.depthPrepassRegistry.Clear(); + viewerData.depthPrepassRenderQueue.Clear(); + { + std::size_t visibilityHash = 5U; + + auto elementIt = viewerData.depthPrepassRenderElements.begin(); + for (auto&& [aabb, elementCount] : viewerData.depthPrepassAABB) + { + if (!frustum.Contains(aabb)) + { + std::advance(elementIt, elementCount); + continue; } + + for (std::size_t i = 0; i < elementCount; ++i) + { + auto& renderElement = *elementIt++; + renderElement->Register(viewerData.depthPrepassRegistry); + viewerData.depthPrepassRenderQueue.Insert(renderElement.get()); + + visibilityHash = CombineHash(visibilityHash, std::hash()(renderElement.get())); + } + } + + viewerData.depthPrepassRenderQueue.Sort([&](const RenderElement* element) + { + return element->ComputeSortingScore(viewerData.depthPrepassRegistry); }); - } - } - m_bakedFrameGraph.Execute(renderFrame); - m_rebuildForwardPass = false; - m_rebuildDepthPrepass = false; - m_rebuildFrameGraph = false; - - const Vector2ui& frameSize = renderFrame.GetSize(); - for (auto&& [viewer, viewerData] : m_viewers) - { - const RenderTarget& renderTarget = viewer->GetRenderTarget(); - Recti renderRegion(0, 0, frameSize.x, frameSize.y); - const ShaderBindingPtr& blitShaderBinding = viewerData.blitShaderBinding; - const std::shared_ptr& sourceTexture = m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment); - - renderFrame.Execute([&](CommandBufferBuilder& builder) - { - builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::FragmentShader, MemoryAccess::ColorWrite, MemoryAccess::ShaderRead, TextureLayout::ColorOutput, TextureLayout::ColorInput, *sourceTexture); - - std::array clearValues; - clearValues[0].color = Color::Black; - clearValues[1].depth = 1.f; - clearValues[1].stencil = 0; - - builder.BeginDebugRegion("Main window rendering", Color::Green); + if (viewerData.depthPrepassVisibilityHash != visibilityHash) { - builder.BeginRenderPass(renderTarget.GetFramebuffer(renderFrame.GetFramebufferIndex()), renderTarget.GetRenderPass(), renderRegion, { clearValues[0], clearValues[1] }); - { - builder.SetScissor(renderRegion); - builder.SetViewport(renderRegion); - - builder.BindPipeline(*graphics->GetBlitPipeline()); - builder.BindVertexBuffer(0, *graphics->GetFullscreenVertexBuffer()); - builder.BindShaderBinding(0, *blitShaderBinding); - - builder.Draw(3); - } - builder.EndRenderPass(); + viewerData.rebuildDepthPrepass = true; + viewerData.depthPrepassVisibilityHash = visibilityHash; } - builder.EndDebugRegion(); - - }, QueueType::Graphics); - } - } - - void ForwardFramePipeline::UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) - { - auto instanceIt = m_renderables.find(worldInstance); - if (instanceIt == m_renderables.end()) - return; - - auto& instancedRenderables = instanceIt->second; - - auto renderableIt = instancedRenderables.find(instancedRenderable); - if (renderableIt == instancedRenderables.end()) - return; - - if (instancedRenderables.size() > 1) - instancedRenderables.erase(renderableIt); - else - { - m_removedWorldInstances.insert(worldInstance); - m_renderables.erase(instanceIt);; - } - - std::size_t matCount = instancedRenderable->GetMaterialCount(); - for (std::size_t i = 0; i < matCount; ++i) - { - if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_depthPassIndex)) - UnregisterMaterialPass(pass); - - if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_forwardPassIndex)) - UnregisterMaterialPass(pass); - } - - m_rebuildDepthPrepass = true; - m_rebuildForwardPass = true; - } - - void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance) - { - m_viewers.erase(viewerInstance); - m_rebuildFrameGraph = true; - } - - BakedFrameGraph ForwardFramePipeline::BuildFrameGraph() - { - FrameGraph frameGraph; - - for (auto&& [viewer, viewerData] : m_viewers) - { - viewerData.colorAttachment = frameGraph.AddAttachment({ - "Color", - PixelFormat::RGBA8 - }); - - viewerData.depthStencilAttachment = frameGraph.AddAttachment({ - "Depth-stencil buffer", - Graphics::Instance()->GetPreferredDepthStencilFormat() - }); - } - - for (auto&& [viewer, viewerData] : m_viewers) - { - FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass"); - depthPrepass.SetDepthStencilOutput(viewerData.depthStencilAttachment); - depthPrepass.SetDepthStencilClear(1.f, 0); - - depthPrepass.SetExecutionCallback([this]() + } + + viewerData.forwardRegistry.Clear(); + viewerData.forwardRenderQueue.Clear(); { - if (m_rebuildForwardPass) - return FramePassExecution::UpdateAndExecute; - else - return FramePassExecution::Execute; - }); - - depthPrepass.SetCommandCallback([this, viewer = viewer](CommandBufferBuilder& builder, const Recti& /*renderRect*/) - { - Recti viewport = viewer->GetViewport(); + std::size_t visibilityHash = 5U; + + auto elementIt = viewerData.forwardRenderElements.begin(); + for (auto&& [aabb, elementCount] : viewerData.forwardAABB) + { + if (!frustum.Contains(aabb)) + { + std::advance(elementIt, elementCount); + continue; + } + + for (std::size_t i = 0; i < elementCount; ++i) + { + auto& renderElement = *elementIt++; + renderElement->Register(viewerData.forwardRegistry); + viewerData.forwardRenderQueue.Insert(renderElement.get()); - builder.SetScissor(viewport); - builder.SetViewport(viewport); + visibilityHash = CombineHash(visibilityHash, std::hash()(renderElement.get())); + } + } + + viewerData.forwardRenderQueue.Sort([&](const RenderElement* element) + { + return element->ComputeSortingScore(viewerData.forwardRegistry); + }); - builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); - - ProcessRenderQueue(builder, m_depthPrepassRenderQueue); - }); - - FramePass& forwardPass = frameGraph.AddPass("Forward pass"); - forwardPass.AddOutput(viewerData.colorAttachment); - forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment); - //forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment); - - forwardPass.SetClearColor(0, Color::Black); - forwardPass.SetDepthStencilClear(1.f, 0); - - forwardPass.SetExecutionCallback([this]() - { - if (m_rebuildForwardPass) - return FramePassExecution::UpdateAndExecute; - else - return FramePassExecution::Execute; - }); - - forwardPass.SetCommandCallback([this, viewer = viewer](CommandBufferBuilder& builder, const Recti& /*renderRect*/) - { - Recti viewport = viewer->GetViewport(); - - builder.SetScissor(viewport); - builder.SetViewport(viewport); - - builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); - - ProcessRenderQueue(builder, m_forwardRenderQueue); - }); - } - - //FIXME: This doesn't handle multiple window viewers - for (auto&& [viewer, viewerData] : m_viewers) - frameGraph.SetBackbufferOutput(viewerData.colorAttachment); - - return frameGraph.Bake(); - } - - void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* material) - { - auto it = m_materials.find(material); - if (it == m_materials.end()) + if (viewerData.forwardVisibilityHash != visibilityHash) + { + viewerData.rebuildForwardPass = true; + viewerData.forwardVisibilityHash = visibilityHash; + } + } + } + + if (m_bakedFrameGraph.Resize(renderFrame)) + { + const std::shared_ptr& sampler = graphics->GetSamplerCache().Get({}); + for (auto&& [_, viewerData] : m_viewers) + { + if (viewerData.blitShaderBinding) + renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding)); + + viewerData.blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0); + viewerData.blitShaderBinding->Update({ + { + 0, + ShaderBinding::TextureBinding { + m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment).get(), + sampler.get() + } + } + }); + } + } + + m_bakedFrameGraph.Execute(renderFrame); + m_rebuildFrameGraph = false; + + const Vector2ui& frameSize = renderFrame.GetSize(); + for (auto&& [viewer, viewerData] : m_viewers) { - 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++; - } - - void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue& renderQueue) - { - if (renderQueue.empty()) - return; - - auto it = renderQueue.begin(); - auto itEnd = renderQueue.end(); - while (it != itEnd) + viewerData.rebuildForwardPass = false; + viewerData.rebuildDepthPrepass = false; + + const RenderTarget& renderTarget = viewer->GetRenderTarget(); + Recti renderRegion(0, 0, frameSize.x, frameSize.y); + const ShaderBindingPtr& blitShaderBinding = viewerData.blitShaderBinding; + const std::shared_ptr& sourceTexture = m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment); + + renderFrame.Execute([&](CommandBufferBuilder& builder) + { + builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::FragmentShader, MemoryAccess::ColorWrite, MemoryAccess::ShaderRead, TextureLayout::ColorOutput, TextureLayout::ColorInput, *sourceTexture); + + std::array clearValues; + clearValues[0].color = Color::Black; + clearValues[1].depth = 1.f; + clearValues[1].stencil = 0; + + builder.BeginDebugRegion("Main window rendering", Color::Green); + { + builder.BeginRenderPass(renderTarget.GetFramebuffer(renderFrame.GetFramebufferIndex()), renderTarget.GetRenderPass(), renderRegion, { clearValues[0], clearValues[1] }); + { + builder.SetScissor(renderRegion); + builder.SetViewport(renderRegion); + + builder.BindPipeline(*graphics->GetBlitPipeline()); + builder.BindVertexBuffer(0, *graphics->GetFullscreenVertexBuffer()); + builder.BindShaderBinding(0, *blitShaderBinding); + + builder.Draw(3); + } + builder.EndRenderPass(); + } + builder.EndDebugRegion(); + + }, QueueType::Graphics); + } + } + + void ForwardFramePipeline::UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) + { + auto instanceIt = m_renderables.find(worldInstance); + if (instanceIt == m_renderables.end()) + return; + + auto& instancedRenderables = instanceIt->second; + + auto renderableIt = instancedRenderables.find(instancedRenderable); + if (renderableIt == instancedRenderables.end()) + return; + + if (instancedRenderables.size() > 1) + instancedRenderables.erase(renderableIt); + else + { + m_removedWorldInstances.insert(worldInstance); + m_renderables.erase(instanceIt);; + } + + std::size_t matCount = instancedRenderable->GetMaterialCount(); + for (std::size_t i = 0; i < matCount; ++i) + { + if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_depthPassIndex)) + UnregisterMaterialPass(pass); + + if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_forwardPassIndex)) + UnregisterMaterialPass(pass); + } + + for (auto&& [viewer, viewerData] : m_viewers) { - 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; - - ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; - elementRenderer.Render(builder, 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); - } -} + viewerData.rebuildDepthPrepass = true; + viewerData.rebuildForwardPass = true; + } + } + + void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance) + { + m_viewers.erase(viewerInstance); + m_rebuildFrameGraph = true; + } + + BakedFrameGraph ForwardFramePipeline::BuildFrameGraph() + { + FrameGraph frameGraph; + + for (auto&& [viewer, viewerData] : m_viewers) + { + viewerData.colorAttachment = frameGraph.AddAttachment({ + "Color", + PixelFormat::RGBA8 + }); + + viewerData.depthStencilAttachment = frameGraph.AddAttachment({ + "Depth-stencil buffer", + Graphics::Instance()->GetPreferredDepthStencilFormat() + }); + } + + for (auto&& [viewer, data] : m_viewers) + { + 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(); + + builder.SetScissor(viewport); + builder.SetViewport(viewport); + + builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); + + ProcessRenderQueue(builder, viewerData.depthPrepassRenderQueue); + }); + + FramePass& forwardPass = frameGraph.AddPass("Forward pass"); + forwardPass.AddOutput(viewerData.colorAttachment); + forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment); + //forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment); + + forwardPass.SetClearColor(0, Color::Black); + 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); + + builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); + + ProcessRenderQueue(builder, viewerData.forwardRenderQueue); + }); + } + + //FIXME: This doesn't handle multiple window viewers + for (auto&& [viewer, viewerData] : m_viewers) + frameGraph.SetBackbufferOutput(viewerData.colorAttachment); + + 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++; + } + + void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue& renderQueue) + { + 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; + + ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; + elementRenderer.Render(builder, 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/Model.cpp b/src/Nazara/Graphics/Model.cpp index 30cefa746..9e49d774b 100644 --- a/src/Nazara/Graphics/Model.cpp +++ b/src/Nazara/Graphics/Model.cpp @@ -13,7 +13,8 @@ namespace Nz { - Model::Model(std::shared_ptr graphicalMesh) : + Model::Model(std::shared_ptr graphicalMesh, const Boxf& aabb) : + InstancedRenderable(aabb), m_graphicalMesh(std::move(graphicalMesh)) { m_submeshes.reserve(m_graphicalMesh->GetSubMeshCount());