From 20a86312ff6ca702077365a250911b6780942016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Mon, 21 Feb 2022 20:47:11 +0100 Subject: [PATCH] Graphics/FramePipeline: Replace maps with memory pools and indices --- examples/DeferredShading/main.cpp | 16 +- examples/GraphicsTest/main.cpp | 25 +- examples/PhysicsDemo/main.cpp | 2 +- .../Graphics/Components/GraphicsComponent.hpp | 12 +- .../Graphics/Components/GraphicsComponent.inl | 38 +- .../Graphics/Components/LightComponent.hpp | 11 +- .../Graphics/Components/LightComponent.inl | 38 +- .../Nazara/Graphics/ForwardFramePipeline.hpp | 42 +- .../Nazara/Graphics/ForwardPipelinePass.hpp | 2 +- include/Nazara/Graphics/FramePipeline.hpp | 23 +- .../Nazara/Graphics/Systems/RenderSystem.hpp | 39 +- src/Nazara/Graphics/ForwardFramePipeline.cpp | 389 ++++++++++-------- src/Nazara/Graphics/ForwardPipelinePass.cpp | 7 +- src/Nazara/Graphics/Systems/RenderSystem.cpp | 308 ++++++++++---- 14 files changed, 598 insertions(+), 354 deletions(-) diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 53e87b0f8..cf339da1f 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -844,13 +844,10 @@ int main() { builder.SetViewport(renderArea); - spaceshipModel.UpdateScissorBox(renderArea); - planeModel.UpdateScissorBox(renderArea); - std::vector> elements; - spaceshipModel.BuildElement(forwardPassIndex, modelInstance1, elements); - spaceshipModel.BuildElement(forwardPassIndex, modelInstance2, elements); - planeModel.BuildElement(forwardPassIndex, planeInstance, elements); + spaceshipModel.BuildElement(forwardPassIndex, modelInstance1, elements, renderArea); + spaceshipModel.BuildElement(forwardPassIndex, modelInstance2, elements, renderArea); + planeModel.BuildElement(forwardPassIndex, planeInstance, elements, renderArea); std::vector> elementPointers; std::vector renderStates(elements.size()); @@ -905,8 +902,6 @@ int main() builder.SetScissor(renderArea); builder.SetViewport(renderArea); - flareSprite.UpdateScissorBox(renderArea); - builder.BindShaderBinding(0, *skyboxShaderBinding); builder.BindIndexBuffer(*cubeMeshGfx->GetIndexBuffer(0)); @@ -916,7 +911,7 @@ int main() builder.DrawIndexed(Nz::SafeCast(cubeMeshGfx->GetIndexCount(0))); std::vector> elements; - flareSprite.BuildElement(forwardPassIndex, flareInstance, elements); + flareSprite.BuildElement(forwardPassIndex, flareInstance, elements, renderArea); std::vector> elementPointers; std::vector renderStates(elements.size()); @@ -942,11 +937,10 @@ int main() Nz::FramePass& occluderPass = graph.AddPass("Occluder pass"); occluderPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder, const Nz::Recti& renderArea) { - builder.SetScissor(renderArea); builder.SetViewport(renderArea); std::vector> elements; - flareSprite.BuildElement(forwardPassIndex, flareInstance, elements); + flareSprite.BuildElement(forwardPassIndex, flareInstance, elements, renderArea); std::vector> elementPointers; std::vector renderStates(elements.size()); diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index c8412f107..156d435e8 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -63,22 +63,21 @@ int main() std::shared_ptr material = std::make_shared(); - std::shared_ptr materialPass = std::make_shared(Nz::PhongLightingMaterial::GetSettings()); - materialPass->EnableDepthBuffer(true); - materialPass->EnableFaceCulling(true); + std::shared_ptr forwardPass = std::make_shared(Nz::PhongLightingMaterial::GetSettings()); + forwardPass->EnableDepthBuffer(true); + forwardPass->EnableFaceCulling(true); - material->AddPass("ForwardPass", materialPass); + material->AddPass("ForwardPass", forwardPass); std::shared_ptr normalMap = Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/normal.png", texParams); - Nz::PhongLightingMaterial phongMat(*materialPass); + Nz::PhongLightingMaterial phongMat(*forwardPass); phongMat.EnableAlphaTest(false); phongMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams)); phongMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png", texParams)); phongMat.SetNormalMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/normal.png", texParams)); Nz::Model model(std::move(gfxMesh), spaceshipMesh->GetAABB()); - model.UpdateScissorBox(Nz::Recti(0, 0, 1920, 1080)); for (std::size_t i = 0; i < model.GetSubMeshCount(); ++i) model.SetMaterial(i, material); @@ -97,12 +96,14 @@ int main() Nz::WorldInstancePtr modelInstance2 = std::make_shared(); modelInstance2->UpdateWorldMatrix(Nz::Matrix4f::Translate(Nz::Vector3f::Forward() * 2 + Nz::Vector3f::Right())); - model.UpdateScissorBox(Nz::Recti(Nz::Vector2i(window.GetSize()))); + Nz::Recti scissorBox(Nz::Vector2i(window.GetSize())); Nz::ForwardFramePipeline framePipeline; - framePipeline.RegisterViewer(&camera, 0); - framePipeline.RegisterInstancedDrawable(modelInstance, &model, 0xFFFFFFFF); - framePipeline.RegisterInstancedDrawable(modelInstance2, &model, 0xFFFFFFFF); + std::size_t cameraIndex = framePipeline.RegisterViewer(&camera, 0); + std::size_t worldInstanceIndex1 = framePipeline.RegisterWorldInstance(modelInstance); + std::size_t worldInstanceIndex2 = framePipeline.RegisterWorldInstance(modelInstance2); + framePipeline.RegisterRenderable(worldInstanceIndex1, &model, 0xFFFFFFFF, scissorBox); + framePipeline.RegisterRenderable(worldInstanceIndex2, &model, 0xFFFFFFFF, scissorBox); std::shared_ptr light = std::make_shared(); light->UpdateColor(Nz::Color::Green); @@ -146,7 +147,7 @@ int main() else if (event.key.virtualKey == Nz::Keyboard::VKey::Space) { modelInstance->UpdateWorldMatrix(Nz::Matrix4f::Translate(viewerPos)); - framePipeline.InvalidateWorldInstance(modelInstance.get()); + framePipeline.InvalidateWorldInstance(worldInstanceIndex1); } break; @@ -218,7 +219,7 @@ int main() viewerInstance.UpdateViewMatrix(Nz::Matrix4f::ViewMatrix(viewerPos, camAngles)); viewerInstance.UpdateEyePosition(viewerPos); - framePipeline.InvalidateViewer(&camera); + framePipeline.InvalidateViewer(cameraIndex); framePipeline.Render(frame); diff --git a/examples/PhysicsDemo/main.cpp b/examples/PhysicsDemo/main.cpp index e939ecbc3..1b9d33681 100644 --- a/examples/PhysicsDemo/main.cpp +++ b/examples/PhysicsDemo/main.cpp @@ -335,7 +335,7 @@ int main() continue; Nz::Vector3f spaceshipPos = node.GetPosition(Nz::CoordSys::Global); - if (spaceshipPos.GetSquaredLength() > Nz::IntegralPow(200.f, 2)) + if (spaceshipPos.GetSquaredLength() > Nz::IntegralPow(20.f, 2)) registry.destroy(entity); } diff --git a/include/Nazara/Graphics/Components/GraphicsComponent.hpp b/include/Nazara/Graphics/Components/GraphicsComponent.hpp index a9072592b..9e6a59b69 100644 --- a/include/Nazara/Graphics/Components/GraphicsComponent.hpp +++ b/include/Nazara/Graphics/Components/GraphicsComponent.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ namespace Nz { public: struct Renderable; + static constexpr std::size_t MaxRenderableCount = 8; inline GraphicsComponent(bool initialyVisible = true); GraphicsComponent(const GraphicsComponent&) = default; @@ -32,7 +34,8 @@ namespace Nz inline void DetachRenderable(const std::shared_ptr& renderable); - inline const std::vector& GetRenderables() const; + inline const Renderable& GetRenderableEntry(std::size_t renderableIndex) const; + inline const std::array& GetRenderables() const; inline const Recti& GetScissorBox() const; inline const WorldInstancePtr& GetWorldInstance() const; @@ -47,8 +50,9 @@ namespace Nz GraphicsComponent& operator=(const GraphicsComponent&) = default; GraphicsComponent& operator=(GraphicsComponent&&) = default; - NazaraSignal(OnRenderableAttached, GraphicsComponent* /*graphicsComponent*/, const Renderable& /*renderable*/); - NazaraSignal(OnRenderableDetach, GraphicsComponent* /*graphicsComponent*/, const Renderable& /*renderable*/); + NazaraSignal(OnRenderableAttached, GraphicsComponent* /*graphicsComponent*/, std::size_t /*renderableIndex*/); + NazaraSignal(OnRenderableDetach, GraphicsComponent* /*graphicsComponent*/, std::size_t /*renderableIndex*/); + NazaraSignal(OnScissorBoxUpdate, GraphicsComponent* /*graphicsComponent*/, const Recti& /*newScissorBox*/); NazaraSignal(OnVisibilityUpdate, GraphicsComponent* /*graphicsComponent*/, bool /*newVisibilityState*/); struct Renderable @@ -58,7 +62,7 @@ namespace Nz }; private: - std::vector m_renderables; + std::array m_renderables; Recti m_scissorBox; WorldInstancePtr m_worldInstance; bool m_isVisible; diff --git a/include/Nazara/Graphics/Components/GraphicsComponent.inl b/include/Nazara/Graphics/Components/GraphicsComponent.inl index e0c1d9a79..e76d6f90f 100644 --- a/include/Nazara/Graphics/Components/GraphicsComponent.inl +++ b/include/Nazara/Graphics/Components/GraphicsComponent.inl @@ -16,19 +16,31 @@ namespace Nz inline void GraphicsComponent::AttachRenderable(std::shared_ptr renderable, UInt32 renderMask) { - auto& entry = m_renderables.emplace_back(); - entry.renderable = std::move(renderable); - entry.renderMask = renderMask; + for (std::size_t i = 0; i < m_renderables.size(); ++i) + { + auto& entry = m_renderables[i]; + if (entry.renderable) + continue; - OnRenderableAttached(this, m_renderables.back()); + entry.renderable = std::move(renderable); + entry.renderMask = renderMask; + + OnRenderableAttached(this, i); + break; + } } inline void GraphicsComponent::Clear() { - for (const auto& renderable : m_renderables) - OnRenderableDetach(this, renderable); + for (std::size_t i = 0; i < m_renderables.size(); ++i) + { + auto& entry = m_renderables[i]; + if (entry.renderable) + continue; - m_renderables.clear(); + OnRenderableDetach(this, i); + entry.renderable.reset(); + } } inline void GraphicsComponent::DetachRenderable(const std::shared_ptr& renderable) @@ -36,13 +48,19 @@ namespace Nz auto it = std::find_if(m_renderables.begin(), m_renderables.end(), [&](const auto& renderableEntry) { return renderableEntry.renderable == renderable; }); if (it != m_renderables.end()) { - OnRenderableDetach(this, *it); + OnRenderableDetach(this, std::distance(m_renderables.begin(), it)); - m_renderables.erase(it); + it->renderable.reset(); } } - inline auto GraphicsComponent::GetRenderables() const -> const std::vector& + inline auto GraphicsComponent::GetRenderableEntry(std::size_t renderableIndex) const -> const Renderable& + { + assert(renderableIndex < m_renderables.size()); + return m_renderables[renderableIndex]; + } + + inline auto GraphicsComponent::GetRenderables() const -> const std::array& { return m_renderables; } diff --git a/include/Nazara/Graphics/Components/LightComponent.hpp b/include/Nazara/Graphics/Components/LightComponent.hpp index 7e9aba747..e9832f215 100644 --- a/include/Nazara/Graphics/Components/LightComponent.hpp +++ b/include/Nazara/Graphics/Components/LightComponent.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ namespace Nz { public: struct LightEntry; + static constexpr std::size_t MaxLightCount = 8; inline LightComponent(bool initialyVisible = true); LightComponent(const LightComponent&) = default; @@ -32,7 +34,8 @@ namespace Nz inline void DetachLight(const std::shared_ptr& renderable); - inline const std::vector& GetLights() const; + inline const LightEntry& GetLightEntry(std::size_t lightIndex) const; + inline const std::array& GetLights() const; inline void Hide(); @@ -43,8 +46,8 @@ namespace Nz LightComponent& operator=(const LightComponent&) = default; LightComponent& operator=(LightComponent&&) = default; - NazaraSignal(OnLightAttached, LightComponent* /*graphicsComponent*/, const LightEntry& /*lightEntry*/); - NazaraSignal(OnLightDetach, LightComponent* /*graphicsComponent*/, const LightEntry& /*lightEntry*/); + NazaraSignal(OnLightAttached, LightComponent* /*graphicsComponent*/, std::size_t /*lightIndex*/); + NazaraSignal(OnLightDetach, LightComponent* /*graphicsComponent*/, std::size_t /*lightIndex*/); NazaraSignal(OnVisibilityUpdate, LightComponent* /*graphicsComponent*/, bool /*newVisibilityState*/); struct LightEntry @@ -54,7 +57,7 @@ namespace Nz }; private: - std::vector m_lightEntries; + std::array m_lightEntries; bool m_isVisible; }; } diff --git a/include/Nazara/Graphics/Components/LightComponent.inl b/include/Nazara/Graphics/Components/LightComponent.inl index 61b2a9595..0ed1a42c1 100644 --- a/include/Nazara/Graphics/Components/LightComponent.inl +++ b/include/Nazara/Graphics/Components/LightComponent.inl @@ -14,19 +14,31 @@ namespace Nz inline void LightComponent::AttachLight(std::shared_ptr light, UInt32 renderMask) { - auto& entry = m_lightEntries.emplace_back(); - entry.light = std::move(light); - entry.renderMask = renderMask; + for (std::size_t i = 0; i < m_lightEntries.size(); ++i) + { + auto& entry = m_lightEntries[i]; + if (entry.light) + continue; - OnLightAttached(this, m_lightEntries.back()); + entry.light = std::move(light); + entry.renderMask = renderMask; + + OnLightAttached(this, i); + break; + } } inline void LightComponent::Clear() { - for (const auto& lightEntry : m_lightEntries) - OnLightDetach(this, lightEntry); + for (std::size_t i = 0; i < m_lightEntries.size(); ++i) + { + auto& entry = m_lightEntries[i]; + if (entry.light) + continue; - m_lightEntries.clear(); + OnLightDetach(this, i); + entry.light.reset(); + } } inline void LightComponent::DetachLight(const std::shared_ptr& light) @@ -34,13 +46,19 @@ namespace Nz auto it = std::find_if(m_lightEntries.begin(), m_lightEntries.end(), [&](const auto& lightEntry) { return lightEntry.light == light; }); if (it != m_lightEntries.end()) { - OnLightDetach(this, *it); + OnLightDetach(this, std::distance(m_lightEntries.begin(), it)); - m_lightEntries.erase(it); + it->light.reset(); } } - inline auto LightComponent::GetLights() const -> const std::vector& + inline auto LightComponent::GetLightEntry(std::size_t lightIndex) const -> const LightEntry& + { + assert(lightIndex < m_lightEntries.size()); + return m_lightEntries[lightIndex]; + } + + inline auto LightComponent::GetLights() const -> const std::array& { return m_lightEntries; } diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 1920fa576..3d7740b6a 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -8,6 +8,7 @@ #define NAZARA_GRAPHICS_FORWARDFRAMEPIPELINE_HPP #include +#include #include #include #include @@ -41,20 +42,27 @@ namespace Nz ForwardFramePipeline(ForwardFramePipeline&&) = delete; ~ForwardFramePipeline(); - void InvalidateViewer(AbstractViewer* viewerInstance) override; - void InvalidateWorldInstance(WorldInstance* worldInstance) override; + void InvalidateViewer(std::size_t viewerIndex) override; + void InvalidateWorldInstance(std::size_t renderableIndex) override; - void RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask) override; - void RegisterLight(std::shared_ptr light, UInt32 renderMask) override; + std::size_t RegisterLight(std::shared_ptr light, UInt32 renderMask) override; void RegisterMaterialPass(MaterialPass* materialPass) override; - void RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override; + std::size_t RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) override; + std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override; + std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) override; void Render(RenderFrame& renderFrame) override; - void UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) override; - void UnregisterLight(Light* light) override; + void UnregisterLight(std::size_t lightIndex) override; void UnregisterMaterialPass(MaterialPass* material) override; - void UnregisterViewer(AbstractViewer* viewerInstance) override; + void UnregisterRenderable(std::size_t renderableIndex) override; + void UnregisterViewer(std::size_t viewerIndex) override; + void UnregisterWorldInstance(std::size_t worldInstance) override; + + void UpdateLightRenderMask(std::size_t lightIndex, UInt32 renderMask); + void UpdateRenderableRenderMask(std::size_t renderableIndex, UInt32 renderMask); + void UpdateRenderableScissorBox(std::size_t renderableIndex, const Recti& scissorBox); + void UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder); ForwardFramePipeline& operator=(const ForwardFramePipeline&) = delete; ForwardFramePipeline& operator=(ForwardFramePipeline&&) = delete; @@ -81,6 +89,9 @@ namespace Nz struct RenderableData { + std::size_t worldInstanceIndex; + const InstancedRenderable* renderable; + Recti scissorBox; UInt32 renderMask = 0; NazaraSlot(InstancedRenderable, OnElementInvalidated, onElementInvalidated); @@ -100,26 +111,27 @@ namespace Nz std::size_t depthStencilAttachment; std::unique_ptr depthPrepass; std::unique_ptr forwardPass; + AbstractViewer* viewer; Int32 renderOrder = 0; RenderQueueRegistry forwardRegistry; RenderQueue forwardRenderQueue; ShaderBindingPtr blitShaderBinding; }; - std::size_t m_forwardPassIndex; - std::unordered_map m_viewers; - std::unordered_map m_lights; std::unordered_map m_activeMaterialPasses; - std::unordered_map> m_renderables; std::unordered_map m_renderTargets; - std::unordered_set m_invalidatedViewerInstances; std::unordered_set m_invalidatedMaterialPasses; - std::unordered_set m_invalidatedWorldInstances; - std::unordered_set m_removedWorldInstances; std::vector m_renderStates; std::vector m_visibleRenderables; std::vector m_visibleLights; BakedFrameGraph m_bakedFrameGraph; + Bitset m_invalidatedViewerInstances; + Bitset m_invalidatedWorldInstances; + Bitset m_removedWorldInstances; + MemoryPool m_renderablePool; + MemoryPool m_lightPool; + MemoryPool m_viewerPool; + MemoryPool m_worldInstances; RenderFrame* m_currentRenderFrame; bool m_rebuildFrameGraph; }; diff --git a/include/Nazara/Graphics/ForwardPipelinePass.hpp b/include/Nazara/Graphics/ForwardPipelinePass.hpp index a873aa170..16a533c54 100644 --- a/include/Nazara/Graphics/ForwardPipelinePass.hpp +++ b/include/Nazara/Graphics/ForwardPipelinePass.hpp @@ -40,7 +40,7 @@ namespace Nz 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 RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass); void UnregisterMaterial(const Material& material); diff --git a/include/Nazara/Graphics/FramePipeline.hpp b/include/Nazara/Graphics/FramePipeline.hpp index d6e22f926..a4fee9b93 100644 --- a/include/Nazara/Graphics/FramePipeline.hpp +++ b/include/Nazara/Graphics/FramePipeline.hpp @@ -37,22 +37,29 @@ namespace Nz 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; + virtual void InvalidateViewer(std::size_t viewerIndex) = 0; + virtual void InvalidateWorldInstance(std::size_t) = 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 std::size_t RegisterLight(std::shared_ptr light, UInt32 renderMask) = 0; virtual void RegisterMaterialPass(MaterialPass* materialPass) = 0; - virtual void RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0; + virtual std::size_t RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) = 0; + virtual std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0; + virtual std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) = 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 UnregisterLight(std::size_t lightIndex) = 0; virtual void UnregisterMaterialPass(MaterialPass* materialPass) = 0; - virtual void UnregisterViewer(AbstractViewer* viewerInstance) = 0; + virtual void UnregisterRenderable(std::size_t renderableIndex) = 0; + virtual void UnregisterViewer(std::size_t viewerIndex) = 0; + virtual void UnregisterWorldInstance(std::size_t worldInstance) = 0; + + virtual void UpdateLightRenderMask(std::size_t lightIndex, UInt32 renderMask) = 0; + virtual void UpdateRenderableRenderMask(std::size_t renderableIndex, UInt32 renderMask) = 0; + virtual void UpdateRenderableScissorBox(std::size_t renderableIndex, const Recti& scissorBox) = 0; + virtual void UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder) = 0; FramePipeline& operator=(const FramePipeline&) = delete; FramePipeline& operator=(FramePipeline&&) noexcept = default; diff --git a/include/Nazara/Graphics/Systems/RenderSystem.hpp b/include/Nazara/Graphics/Systems/RenderSystem.hpp index 00312edce..87cf0fd06 100644 --- a/include/Nazara/Graphics/Systems/RenderSystem.hpp +++ b/include/Nazara/Graphics/Systems/RenderSystem.hpp @@ -8,11 +8,13 @@ #define NAZARA_GRAPHICS_SYSTEMS_RENDERSYSTEM_HPP #include +#include #include #include #include #include #include +#include #include #include #include @@ -47,19 +49,33 @@ namespace Nz struct CameraEntity { + entt::entity entity; + std::size_t poolIndex; + std::size_t viewerIndex; + NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation); }; struct GraphicsEntity { + entt::entity entity; + std::array renderableIndices; + std::size_t poolIndex; + std::size_t worldInstanceIndex; + NazaraSlot(GraphicsComponent, OnRenderableAttached, onRenderableAttached); NazaraSlot(GraphicsComponent, OnRenderableDetach, onRenderableDetach); + NazaraSlot(GraphicsComponent, OnScissorBoxUpdate, onScissorBoxUpdate); NazaraSlot(GraphicsComponent, OnVisibilityUpdate, onVisibilityUpdate); NazaraSlot(Node, OnNodeInvalidation, onNodeInvalidation); }; struct LightEntity { + entt::entity entity; + std::array lightIndices; + std::size_t poolIndex; + NazaraSlot(LightComponent, OnLightAttached, onLightAttached); NazaraSlot(LightComponent, OnLightDetach, onLightDetach); NazaraSlot(LightComponent, OnVisibilityUpdate, onVisibilityUpdate); @@ -73,17 +89,20 @@ namespace Nz entt::observer m_cameraConstructObserver; entt::observer m_graphicsConstructObserver; entt::observer m_lightConstructObserver; - std::set m_invalidatedCameraNode; - std::set m_invalidatedGfxWorldNode; - std::set m_invalidatedLightWorldNode; + std::set m_invalidatedCameraNode; + std::set m_invalidatedGfxWorldNode; + std::set m_invalidatedLightWorldNode; std::unique_ptr m_pipeline; - std::unordered_map m_cameraEntities; - std::unordered_map m_graphicsEntities; - std::unordered_map m_lightEntities; - std::unordered_set m_newlyHiddenGfxEntities; - std::unordered_set m_newlyVisibleGfxEntities; - std::unordered_set m_newlyHiddenLightEntities; - std::unordered_set m_newlyVisibleLightEntities; + std::unordered_map m_cameraEntities; + std::unordered_map m_graphicsEntities; + std::unordered_map m_lightEntities; + std::unordered_set m_newlyHiddenGfxEntities; + std::unordered_set m_newlyVisibleGfxEntities; + std::unordered_set m_newlyHiddenLightEntities; + std::unordered_set m_newlyVisibleLightEntities; + MemoryPool m_cameraEntityPool; + MemoryPool m_graphicsEntityPool; + MemoryPool m_lightEntityPool; }; } diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index a702ed106..e68d08422 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -27,117 +27,49 @@ namespace Nz { ForwardFramePipeline::ForwardFramePipeline() : + m_renderablePool(4096), + m_lightPool(64), + m_viewerPool(8), + m_worldInstances(2048), m_rebuildFrameGraph(true) { - auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry(); - m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass"); } ForwardFramePipeline::~ForwardFramePipeline() { // Force viewer passes to unregister their materials - m_viewers.clear(); + m_viewerPool.Clear(); } - void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance) + void ForwardFramePipeline::InvalidateViewer(std::size_t viewerIndex) { - m_invalidatedViewerInstances.insert(viewerInstance); + m_invalidatedViewerInstances.Set(viewerIndex); } - void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance) + void ForwardFramePipeline::InvalidateWorldInstance(std::size_t worldInstanceIndex) { - m_invalidatedWorldInstances.insert(worldInstance); + m_invalidatedWorldInstances.Set(worldInstanceIndex); } - void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask) + std::size_t ForwardFramePipeline::RegisterLight(std::shared_ptr light, UInt32 renderMask) { - 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.renderMask = renderMask; - - renderableData.onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [=](InstancedRenderable* /*instancedRenderable*/) - { - // TODO: Invalidate only relevant viewers and passes - for (auto&& [viewer, viewerData] : m_viewers) - { - UInt32 viewerRenderMask = viewer->GetRenderMask(); - - if (viewerRenderMask & renderMask) - { - if (viewerData.depthPrepass) - viewerData.depthPrepass->ForceInvalidation(); - - viewerData.forwardPass->ForceInvalidation(); - } - } - }); - - renderableData.onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr& newMaterial) - { - if (newMaterial) - { - for (auto&& [viewer, viewerData] : m_viewers) - { - if (viewerData.depthPrepass) - viewerData.depthPrepass->RegisterMaterial(*newMaterial); - - viewerData.forwardPass->RegisterMaterial(*newMaterial); - } - } - - const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex); - if (prevMaterial) - { - for (auto&& [viewer, viewerData] : m_viewers) - { - if (viewerData.depthPrepass) - viewerData.depthPrepass->UnregisterMaterial(*prevMaterial); - - viewerData.forwardPass->UnregisterMaterial(*prevMaterial); - } - } - }); - - std::size_t matCount = instancedRenderable->GetMaterialCount(); - for (std::size_t i = 0; i < matCount; ++i) - { - if (Material* mat = instancedRenderable->GetMaterial(i).get()) - { - for (auto&& [viewer, viewerData] : m_viewers) - { - if (viewerData.depthPrepass) - viewerData.depthPrepass->RegisterMaterial(*mat); - - viewerData.forwardPass->RegisterMaterial(*mat); - } - } - } - } - } - - void ForwardFramePipeline::RegisterLight(std::shared_ptr light, UInt32 renderMask) - { - auto& lightData = m_lights[light.get()]; - lightData.light = std::move(light); - lightData.renderMask = renderMask; - lightData.onLightInvalidated.Connect(lightData.light->OnLightDataInvalided, [=](Light*) + std::size_t lightIndex; + LightData* lightData = m_lightPool.Allocate(lightIndex); + lightData->light = std::move(light); + lightData->renderMask = renderMask; + 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) + for (auto& viewerData : m_viewerPool) { - UInt32 viewerRenderMask = viewer->GetRenderMask(); + UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask(); if (viewerRenderMask & renderMask) viewerData.forwardPass->ForceInvalidation(); } }); + + return lightIndex; } void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* materialPass) @@ -156,16 +88,100 @@ namespace Nz it->second.usedCount++; } - - void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) + + std::size_t ForwardFramePipeline::RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) { - auto& viewerData = m_viewers.emplace(viewerInstance, ViewerData{}).first->second; + std::size_t renderableIndex; + RenderableData* renderableData = m_renderablePool.Allocate(renderableIndex); + renderableData->renderable = instancedRenderable; + renderableData->renderMask = renderMask; + renderableData->scissorBox = scissorBox; + renderableData->worldInstanceIndex = worldInstanceIndex; + + renderableData->onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [=](InstancedRenderable* /*instancedRenderable*/) + { + // TODO: Invalidate only relevant viewers and passes + for (auto& viewerData : m_viewerPool) + { + UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask(); + + if (viewerRenderMask & renderMask) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->ForceInvalidation(); + + viewerData.forwardPass->ForceInvalidation(); + } + } + }); + + renderableData->onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr& newMaterial) + { + if (newMaterial) + { + for (auto& viewerData : m_viewerPool) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->RegisterMaterial(*newMaterial); + + viewerData.forwardPass->RegisterMaterial(*newMaterial); + } + } + + const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex); + if (prevMaterial) + { + for (auto& viewerData : m_viewerPool) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->UnregisterMaterial(*prevMaterial); + + viewerData.forwardPass->UnregisterMaterial(*prevMaterial); + } + } + }); + + std::size_t matCount = instancedRenderable->GetMaterialCount(); + for (std::size_t i = 0; i < matCount; ++i) + { + if (Material* mat = instancedRenderable->GetMaterial(i).get()) + { + for (auto& viewerData : m_viewerPool) + { + if (viewerData.depthPrepass) + viewerData.depthPrepass->RegisterMaterial(*mat); + + viewerData.forwardPass->RegisterMaterial(*mat); + } + } + } + + return renderableIndex; + } + + std::size_t ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) + { + std::size_t viewerIndex; + auto& viewerData = *m_viewerPool.Allocate(viewerIndex); viewerData.renderOrder = renderOrder; viewerData.depthPrepass = std::make_unique(*this, viewerInstance); viewerData.forwardPass = std::make_unique(*this, viewerInstance); + viewerData.viewer = viewerInstance; - m_invalidatedViewerInstances.insert(viewerInstance); + m_invalidatedViewerInstances.UnboundedSet(viewerIndex); m_rebuildFrameGraph = true; + + return viewerIndex; + } + + std::size_t ForwardFramePipeline::RegisterWorldInstance(WorldInstancePtr worldInstance) + { + std::size_t worldInstanceIndex; + m_worldInstances.Allocate(worldInstanceIndex, std::move(worldInstance)); + + m_invalidatedWorldInstances.UnboundedSet(worldInstanceIndex); + + return worldInstanceIndex; } void ForwardFramePipeline::Render(RenderFrame& renderFrame) @@ -174,8 +190,13 @@ namespace Nz Graphics* graphics = Graphics::Instance(); - renderFrame.PushForRelease(std::move(m_removedWorldInstances)); - m_removedWorldInstances.clear(); + // Destroy world instances at the end of the frame + for (std::size_t worldInstanceIndex = m_removedWorldInstances.FindFirst(); worldInstanceIndex != m_removedWorldInstances.npos; worldInstanceIndex = m_removedWorldInstances.FindNext(worldInstanceIndex)) + { + renderFrame.PushForRelease(*m_worldInstances.RetrieveFromIndex(worldInstanceIndex)); + m_worldInstances.Free(worldInstanceIndex); + } + m_removedWorldInstances.Clear(); if (m_rebuildFrameGraph) { @@ -192,15 +213,19 @@ namespace Nz { builder.PreTransferBarrier(); - for (AbstractViewer* viewer : m_invalidatedViewerInstances) - viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder); + for (std::size_t viewerIndex = m_invalidatedViewerInstances.FindFirst(); viewerIndex != m_invalidatedViewerInstances.npos; viewerIndex = m_invalidatedViewerInstances.FindNext(viewerIndex)) + { + ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex); + viewerData->viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder); + } + m_invalidatedViewerInstances.Reset(); - m_invalidatedViewerInstances.clear(); - - for (WorldInstance* worldInstance : m_invalidatedWorldInstances) + for (std::size_t worldInstanceIndex = m_invalidatedWorldInstances.FindFirst(); worldInstanceIndex != m_invalidatedWorldInstances.npos; worldInstanceIndex = m_invalidatedWorldInstances.FindNext(worldInstanceIndex)) + { + WorldInstancePtr& worldInstance = *m_worldInstances.RetrieveFromIndex(worldInstanceIndex); worldInstance->UpdateBuffers(uploadPool, builder); - - m_invalidatedWorldInstances.clear(); + } + m_invalidatedWorldInstances.Reset(); for (MaterialPass* materialPass : m_invalidatedMaterialPasses) materialPass->Update(renderFrame, builder); @@ -218,61 +243,53 @@ namespace Nz }; // Render queues handling - for (auto&& [viewer, data] : m_viewers) + for (auto& viewerData : m_viewerPool) { - auto& viewerData = data; - - UInt32 renderMask = viewer->GetRenderMask(); + UInt32 renderMask = viewerData.viewer->GetRenderMask(); // Frustum culling - const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix(); + const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix(); Frustumf frustum = Frustumf::Extract(viewProjMatrix); std::size_t visibilityHash = 5U; m_visibleRenderables.clear(); - for (const auto& [worldInstance, renderables] : m_renderables) + for (const RenderableData& renderableData : m_renderablePool) { - bool isInstanceVisible = false; + if ((renderMask & renderableData.renderMask) == 0) + continue; - for (const auto& [renderable, renderableData] : renderables) - { - if ((renderMask & renderableData.renderMask) == 0) - continue; + WorldInstancePtr& worldInstance = *m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex); - // Get global AABB - BoundingVolumef boundingVolume(renderable->GetAABB()); - boundingVolume.Update(worldInstance->GetWorldMatrix()); + // Get global AABB + BoundingVolumef boundingVolume(renderableData.renderable->GetAABB()); + boundingVolume.Update(worldInstance->GetWorldMatrix()); - if (!frustum.Contains(boundingVolume)) - continue; + if (!frustum.Contains(boundingVolume)) + continue; - auto& visibleRenderable = m_visibleRenderables.emplace_back(); - visibleRenderable.instancedRenderable = renderable; - visibleRenderable.worldInstance = worldInstance.get(); + auto& visibleRenderable = m_visibleRenderables.emplace_back(); + visibleRenderable.instancedRenderable = renderableData.renderable; + visibleRenderable.scissorBox = renderableData.scissorBox; + visibleRenderable.worldInstance = worldInstance.get(); - isInstanceVisible = true; - visibilityHash = CombineHash(visibilityHash, std::hash()(renderable)); - } - - if (isInstanceVisible) - visibilityHash = CombineHash(visibilityHash, std::hash()(worldInstance.get())); + visibilityHash = CombineHash(visibilityHash, std::hash()(&renderableData)); } // 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) + for (const LightData& lightData : m_lightPool) { - const BoundingVolumef& boundingVolume = light->GetBoundingVolume(); + const BoundingVolumef& boundingVolume = lightData.light->GetBoundingVolume(); // TODO: Use more precise tests for point lights (frustum/sphere is cheap) if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume)) { - m_visibleLights.push_back(light); - visibilityHash = CombineHash(visibilityHash, std::hash()(light)); + m_visibleLights.push_back(lightData.light.get()); + visibilityHash = CombineHash(visibilityHash, std::hash()(lightData.light.get())); } } @@ -285,7 +302,7 @@ namespace Nz if (m_bakedFrameGraph.Resize(renderFrame)) { const std::shared_ptr& sampler = graphics->GetSamplerCache().Get({}); - for (auto&& [_, viewerData] : m_viewers) + for (auto& viewerData : m_viewerPool) { if (viewerData.blitShaderBinding) renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding)); @@ -362,43 +379,9 @@ namespace Nz } } - void ForwardFramePipeline::UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable) + void ForwardFramePipeline::UnregisterLight(std::size_t lightIndex) { - 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) - { - for (auto&& [viewer, viewerData] : m_viewers) - { - const auto& material = instancedRenderable->GetMaterial(i); - if (viewerData.depthPrepass) - viewerData.depthPrepass->UnregisterMaterial(*material); - - viewerData.forwardPass->UnregisterMaterial(*material); - } - } - } - - void ForwardFramePipeline::UnregisterLight(Light* light) - { - m_lights.erase(light); + m_lightPool.Free(lightIndex); } void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* materialPass) @@ -412,17 +395,72 @@ namespace Nz m_activeMaterialPasses.erase(materialPass); } - void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance) + void ForwardFramePipeline::UnregisterRenderable(std::size_t renderableIndex) { - m_viewers.erase(viewerInstance); + RenderableData& renderable = *m_renderablePool.RetrieveFromIndex(renderableIndex); + + std::size_t matCount = renderable.renderable->GetMaterialCount(); + for (std::size_t i = 0; i < matCount; ++i) + { + for (auto& viewerData : m_viewerPool) + { + const auto& material = renderable.renderable->GetMaterial(i); + if (viewerData.depthPrepass) + viewerData.depthPrepass->UnregisterMaterial(*material); + + viewerData.forwardPass->UnregisterMaterial(*material); + } + } + + m_renderablePool.Free(renderableIndex); + } + + void ForwardFramePipeline::UnregisterViewer(std::size_t viewerIndex) + { + m_viewerPool.Free(viewerIndex); + m_invalidatedViewerInstances.Reset(viewerIndex); m_rebuildFrameGraph = true; } + void ForwardFramePipeline::UnregisterWorldInstance(std::size_t worldInstance) + { + // Defer world instance release + m_removedWorldInstances.UnboundedSet(worldInstance); + } + + void ForwardFramePipeline::UpdateLightRenderMask(std::size_t lightIndex, UInt32 renderMask) + { + LightData* lightData = m_lightPool.RetrieveFromIndex(lightIndex); + lightData->renderMask = renderMask; + } + + void ForwardFramePipeline::UpdateRenderableRenderMask(std::size_t renderableIndex, UInt32 renderMask) + { + RenderableData* renderableData = m_renderablePool.RetrieveFromIndex(renderableIndex); + renderableData->renderMask = renderMask; + } + + void ForwardFramePipeline::UpdateRenderableScissorBox(std::size_t renderableIndex, const Recti& scissorBox) + { + RenderableData* renderableData = m_renderablePool.RetrieveFromIndex(renderableIndex); + renderableData->scissorBox = scissorBox; + } + + void ForwardFramePipeline::UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder) + { + ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex); + if (viewerData->renderOrder != renderOrder) + { + viewerData->renderOrder = renderOrder; + m_rebuildFrameGraph = true; + } + } + BakedFrameGraph ForwardFramePipeline::BuildFrameGraph() { FrameGraph frameGraph; - for (auto&& [viewer, viewerData] : m_viewers) + for (auto& viewerData : m_viewerPool) { viewerData.colorAttachment = frameGraph.AddAttachment({ "Color", @@ -433,26 +471,21 @@ namespace Nz "Depth-stencil buffer", Graphics::Instance()->GetPreferredDepthStencilFormat() }); - } - - for (auto&& [viewer, data] : m_viewers) - { - auto& viewerData = data; if (viewerData.depthPrepass) viewerData.depthPrepass->RegisterToFrameGraph(frameGraph, viewerData.depthStencilAttachment); - viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.colorAttachment, viewerData.depthStencilAttachment); + viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.colorAttachment, viewerData.depthStencilAttachment, viewerData.depthPrepass != nullptr); } using ViewerPair = std::pair; - StackArray viewers = NazaraStackArray(ViewerPair, m_viewers.size()); + StackArray viewers = NazaraStackArray(ViewerPair, m_viewerPool.size()); auto viewerIt = viewers.begin(); - for (auto&& [viewer, viewerData] : m_viewers) + for (auto& viewerData : m_viewerPool) { - const RenderTarget& renderTarget = viewer->GetRenderTarget(); + const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget(); *viewerIt++ = std::make_pair(&renderTarget, &viewerData); } diff --git a/src/Nazara/Graphics/ForwardPipelinePass.cpp b/src/Nazara/Graphics/ForwardPipelinePass.cpp index 3fa5aaf60..3e1e38769 100644 --- a/src/Nazara/Graphics/ForwardPipelinePass.cpp +++ b/src/Nazara/Graphics/ForwardPipelinePass.cpp @@ -265,11 +265,14 @@ namespace Nz it->second.usedCount++; } - void ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex) + void ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass) { FramePass& forwardPass = frameGraph.AddPass("Forward pass"); forwardPass.AddOutput(colorBufferIndex); - forwardPass.SetDepthStencilInput(depthBufferIndex); + if (hasDepthPrepass) + forwardPass.SetDepthStencilInput(depthBufferIndex); + else + forwardPass.SetDepthStencilOutput(depthBufferIndex); forwardPass.SetClearColor(0, m_viewer->GetClearColor()); forwardPass.SetDepthStencilClear(1.f, 0); diff --git a/src/Nazara/Graphics/Systems/RenderSystem.cpp b/src/Nazara/Graphics/Systems/RenderSystem.cpp index a59e19558..af815ec8d 100644 --- a/src/Nazara/Graphics/Systems/RenderSystem.cpp +++ b/src/Nazara/Graphics/Systems/RenderSystem.cpp @@ -19,7 +19,10 @@ namespace Nz RenderSystem::RenderSystem(entt::registry& registry) : m_cameraConstructObserver(registry, entt::collector.group()), m_graphicsConstructObserver(registry, entt::collector.group()), - m_lightConstructObserver(registry, entt::collector.group()) + m_lightConstructObserver(registry, entt::collector.group()), + m_cameraEntityPool(8), + m_graphicsEntityPool(1024), + m_lightEntityPool(32) { m_cameraDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnCameraDestroy>(this); m_graphicsDestroyConnection = registry.on_destroy().connect<&RenderSystem::OnGraphicsDestroy>(this); @@ -47,16 +50,20 @@ namespace Nz CameraComponent& entityCamera = registry.get(entity); NodeComponent& entityNode = registry.get(entity); - m_pipeline->RegisterViewer(&entityCamera, entityCamera.GetRenderOrder()); + std::size_t poolIndex; + CameraEntity* cameraEntity = m_cameraEntityPool.Allocate(poolIndex); + cameraEntity->poolIndex = poolIndex; + cameraEntity->entity = entity; + cameraEntity->viewerIndex = m_pipeline->RegisterViewer(&entityCamera, entityCamera.GetRenderOrder()); + cameraEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, cameraEntity](const Node* /*node*/) + { + m_invalidatedCameraNode.insert(cameraEntity); + }); - m_invalidatedCameraNode.insert(entity); + m_invalidatedCameraNode.insert(cameraEntity); assert(m_cameraEntities.find(entity) == m_cameraEntities.end()); - auto& cameraEntity = m_cameraEntities[entity]; - cameraEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* /*node*/) - { - m_invalidatedCameraNode.insert(entity); - }); + m_cameraEntities.emplace(entity, cameraEntity); }); m_graphicsConstructObserver.each([&](entt::entity entity) @@ -64,53 +71,77 @@ namespace Nz GraphicsComponent& entityGfx = registry.get(entity); NodeComponent& entityNode = registry.get(entity); - if (entityGfx.IsVisible()) + std::size_t poolIndex; + GraphicsEntity* graphicsEntity = m_graphicsEntityPool.Allocate(poolIndex); + graphicsEntity->entity = entity; + graphicsEntity->poolIndex = poolIndex; + graphicsEntity->worldInstanceIndex = m_pipeline->RegisterWorldInstance(entityGfx.GetWorldInstance()); + graphicsEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, graphicsEntity](const Node* /*node*/) { - const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); - for (const auto& renderableEntry : entityGfx.GetRenderables()) - m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); - } - - m_invalidatedGfxWorldNode.insert(entity); - - assert(m_graphicsEntities.find(entity) == m_graphicsEntities.end()); - auto& graphicsEntity = m_graphicsEntities[entity]; - graphicsEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* /*node*/) - { - m_invalidatedGfxWorldNode.insert(entity); + m_invalidatedGfxWorldNode.insert(graphicsEntity); }); - graphicsEntity.onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry) + graphicsEntity->onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this, graphicsEntity](GraphicsComponent* gfx, std::size_t renderableIndex) { if (!gfx->IsVisible()) return; - const WorldInstancePtr& worldInstance = gfx->GetWorldInstance(); - m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); + const auto& renderableEntry = gfx->GetRenderableEntry(renderableIndex); + graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, gfx->GetScissorBox()); }); - graphicsEntity.onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry) + graphicsEntity->onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this, graphicsEntity](GraphicsComponent* gfx, std::size_t renderableIndex) { if (!gfx->IsVisible()) return; - const WorldInstancePtr& worldInstance = gfx->GetWorldInstance(); - m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get()); + m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]); }); - graphicsEntity.onVisibilityUpdate.Connect(entityGfx.OnVisibilityUpdate, [this, entity](GraphicsComponent* /*gfx*/, bool isVisible) + graphicsEntity->onScissorBoxUpdate.Connect(entityGfx.OnScissorBoxUpdate, [this, graphicsEntity](GraphicsComponent* gfx, const Recti& scissorBox) + { + if (!gfx->IsVisible()) + return; + + for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex) + { + const auto& renderableEntry = gfx->GetRenderableEntry(renderableIndex); + if (!renderableEntry.renderable) + continue; + + m_pipeline->UpdateRenderableScissorBox(graphicsEntity->renderableIndices[renderableIndex], scissorBox); + } + }); + + graphicsEntity->onVisibilityUpdate.Connect(entityGfx.OnVisibilityUpdate, [this, graphicsEntity](GraphicsComponent* /*gfx*/, bool isVisible) { if (isVisible) { - m_newlyHiddenGfxEntities.erase(entity); - m_newlyVisibleGfxEntities.insert(entity); + m_newlyHiddenGfxEntities.erase(graphicsEntity); + m_newlyVisibleGfxEntities.insert(graphicsEntity); } else { - m_newlyHiddenGfxEntities.insert(entity); - m_newlyVisibleGfxEntities.erase(entity); + m_newlyHiddenGfxEntities.insert(graphicsEntity); + m_newlyVisibleGfxEntities.erase(graphicsEntity); } }); + m_invalidatedGfxWorldNode.insert(graphicsEntity); + + if (entityGfx.IsVisible()) + { + for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex) + { + const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); + if (!renderableEntry.renderable) + continue; + + graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox()); + } + } + + assert(m_graphicsEntities.find(entity) == m_graphicsEntities.end()); + m_graphicsEntities.emplace(entity, graphicsEntity); }); m_lightConstructObserver.each([&](entt::entity entity) @@ -118,50 +149,67 @@ namespace Nz LightComponent& entityLight = registry.get(entity); NodeComponent& entityNode = registry.get(entity); - if (entityLight.IsVisible()) + std::size_t poolIndex; + LightEntity* lightEntity = m_lightEntityPool.Allocate(poolIndex); + lightEntity->entity = entity; + lightEntity->poolIndex = poolIndex; + lightEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, lightEntity](const Node* /*node*/) { - for (const auto& lightEntry : entityLight.GetLights()) - m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); - } - - m_invalidatedLightWorldNode.insert(entity); - - assert(m_lightEntities.find(entity) == m_lightEntities.end()); - auto& lightEntity = m_lightEntities[entity]; - lightEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* /*node*/) - { - m_invalidatedLightWorldNode.insert(entity); + m_invalidatedLightWorldNode.insert(lightEntity); }); - lightEntity.onLightAttached.Connect(entityLight.OnLightAttached, [this](LightComponent* light, const LightComponent::LightEntry& lightEntry) + lightEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, lightEntity](const Node* /*node*/) + { + m_invalidatedLightWorldNode.insert(lightEntity); + }); + + lightEntity->onLightAttached.Connect(entityLight.OnLightAttached, [this, lightEntity](LightComponent* light, std::size_t lightIndex) { if (!light->IsVisible()) return; - m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); + const auto& lightEntry = light->GetLightEntry(lightIndex); + lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); }); - lightEntity.onLightDetach.Connect(entityLight.OnLightDetach, [this](LightComponent* light, const LightComponent::LightEntry& lightEntry) + lightEntity->onLightDetach.Connect(entityLight.OnLightDetach, [this, lightEntity](LightComponent* light, std::size_t lightIndex) { if (!light->IsVisible()) return; - m_pipeline->UnregisterLight(lightEntry.light.get()); + m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]); }); - lightEntity.onVisibilityUpdate.Connect(entityLight.OnVisibilityUpdate, [this, entity](LightComponent* /*light*/, bool isVisible) + lightEntity->onVisibilityUpdate.Connect(entityLight.OnVisibilityUpdate, [this, lightEntity](LightComponent* /*light*/, bool isVisible) { if (isVisible) { - m_newlyHiddenLightEntities.erase(entity); - m_newlyVisibleLightEntities.insert(entity); + m_newlyHiddenLightEntities.erase(lightEntity); + m_newlyVisibleLightEntities.insert(lightEntity); } else { - m_newlyHiddenLightEntities.insert(entity); - m_newlyVisibleLightEntities.erase(entity); + m_newlyHiddenLightEntities.insert(lightEntity); + m_newlyVisibleLightEntities.erase(lightEntity); } }); + + m_invalidatedLightWorldNode.insert(lightEntity); + + if (entityLight.IsVisible()) + { + for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex) + { + const auto& lightEntry = entityLight.GetLightEntry(lightIndex); + if (!lightEntry.light) + continue; + + lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); + } + } + + assert(m_lightEntities.find(entity) == m_lightEntities.end()); + m_lightEntities.emplace(entity, lightEntity); }); UpdateVisibility(registry); @@ -172,45 +220,74 @@ namespace Nz void RenderSystem::OnCameraDestroy(entt::registry& registry, entt::entity entity) { - m_cameraEntities.erase(entity); - m_invalidatedCameraNode.erase(entity); + auto it = m_cameraEntities.find(entity); + if (it == m_cameraEntities.end()) + return; - CameraComponent& entityCamera = registry.get(entity); - m_pipeline->UnregisterViewer(&entityCamera); + CameraEntity* cameraEntity = it->second; + + m_cameraEntities.erase(it); + m_invalidatedCameraNode.erase(cameraEntity); + m_pipeline->UnregisterViewer(cameraEntity->viewerIndex); + + m_cameraEntityPool.Free(cameraEntity->poolIndex); } void RenderSystem::OnGraphicsDestroy(entt::registry& registry, entt::entity entity) { + auto it = m_graphicsEntities.find(entity); + if (it == m_graphicsEntities.end()) + return; + + GraphicsEntity* graphicsEntity = it->second; + m_graphicsEntities.erase(entity); - m_invalidatedGfxWorldNode.erase(entity); - m_newlyHiddenGfxEntities.erase(entity); - m_newlyVisibleGfxEntities.erase(entity); + m_invalidatedGfxWorldNode.erase(graphicsEntity); + m_newlyHiddenGfxEntities.erase(graphicsEntity); + m_newlyVisibleGfxEntities.erase(graphicsEntity); GraphicsComponent& entityGfx = registry.get(entity); - const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); - for (const auto& renderableEntry : entityGfx.GetRenderables()) - m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get()); + for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) + { + const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); + if (!renderableEntry.renderable) + continue; + + m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]); + } + m_pipeline->UnregisterWorldInstance(graphicsEntity->worldInstanceIndex); + + m_graphicsEntityPool.Free(graphicsEntity->poolIndex); } void RenderSystem::OnLightDestroy(entt::registry& registry, entt::entity entity) { + auto it = m_lightEntities.find(entity); + if (it == m_lightEntities.end()) + return; + + LightEntity* lightEntity = it->second; + m_lightEntities.erase(entity); - m_invalidatedLightWorldNode.erase(entity); - m_newlyHiddenLightEntities.erase(entity); - m_newlyVisibleLightEntities.erase(entity); + m_invalidatedLightWorldNode.erase(lightEntity); + m_newlyHiddenLightEntities.erase(lightEntity); + m_newlyVisibleLightEntities.erase(lightEntity); LightComponent& entityLight = registry.get(entity); - for (const auto& lightEntry : entityLight.GetLights()) - m_pipeline->UnregisterLight(lightEntry.light.get()); + for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex) + { + const auto& lightEntry = entityLight.GetLightEntry(lightIndex); + if (!lightEntry.light) + continue; + + m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]); + } + + m_lightEntityPool.Free(lightEntity->poolIndex); } void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity) { - m_newlyHiddenGfxEntities.erase(entity); - m_newlyVisibleGfxEntities.erase(entity); - m_newlyHiddenLightEntities.erase(entity); - m_newlyVisibleLightEntities.erase(entity); - if (registry.try_get(entity)) OnCameraDestroy(registry, entity); @@ -223,8 +300,10 @@ namespace Nz void RenderSystem::UpdateInstances(entt::registry& registry) { - for (entt::entity entity : m_invalidatedCameraNode) + for (CameraEntity* cameraEntity : m_invalidatedCameraNode) { + entt::entity entity = cameraEntity->entity; + const NodeComponent& entityNode = registry.get(entity); CameraComponent& entityCamera = registry.get(entity); @@ -234,24 +313,28 @@ namespace Nz viewerInstance.UpdateEyePosition(cameraPosition); viewerInstance.UpdateViewMatrix(Nz::Matrix4f::ViewMatrix(cameraPosition, entityNode.GetRotation(CoordSys::Global))); - m_pipeline->InvalidateViewer(&entityCamera); + m_pipeline->InvalidateViewer(cameraEntity->viewerIndex); } m_invalidatedCameraNode.clear(); - for (entt::entity entity : m_invalidatedGfxWorldNode) + for (GraphicsEntity* graphicsEntity : m_invalidatedGfxWorldNode) { + entt::entity entity = graphicsEntity->entity; + const NodeComponent& entityNode = registry.get(entity); GraphicsComponent& entityGraphics = registry.get(entity); const WorldInstancePtr& worldInstance = entityGraphics.GetWorldInstance(); worldInstance->UpdateWorldMatrix(entityNode.GetTransformMatrix()); - m_pipeline->InvalidateWorldInstance(worldInstance.get()); + m_pipeline->InvalidateWorldInstance(graphicsEntity->worldInstanceIndex); } m_invalidatedGfxWorldNode.clear(); - for (entt::entity entity : m_invalidatedLightWorldNode) + for (LightEntity* lightEntity : m_invalidatedLightWorldNode) { + entt::entity entity = lightEntity->entity; + const NodeComponent& entityNode = registry.get(entity); LightComponent& entityLight = registry.get(entity); @@ -260,7 +343,12 @@ namespace Nz const Vector3f& scale = entityNode.GetScale(CoordSys::Global); for (const auto& lightEntry : entityLight.GetLights()) + { + if (!lightEntry.light) + continue; + lightEntry.light->UpdateTransform(position, rotation, scale); + } } m_invalidatedLightWorldNode.clear(); } @@ -268,25 +356,69 @@ namespace Nz void RenderSystem::UpdateVisibility(entt::registry& registry) { // Unregister drawable for hidden entities - for (entt::entity entity : m_newlyHiddenGfxEntities) + for (GraphicsEntity* graphicsEntity : m_newlyHiddenGfxEntities) { - GraphicsComponent& entityGfx = registry.get(entity); + GraphicsComponent& entityGfx = registry.get(graphicsEntity->entity); - const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); - for (const auto& renderableEntry : entityGfx.GetRenderables()) - m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get()); + for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) + { + const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); + if (!renderableEntry.renderable) + continue; + + m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]); + } } m_newlyHiddenGfxEntities.clear(); // Register drawable for newly visible entities - for (entt::entity entity : m_newlyVisibleGfxEntities) + for (GraphicsEntity* graphicsEntity : m_newlyVisibleGfxEntities) { - GraphicsComponent& entityGfx = registry.get(entity); + GraphicsComponent& entityGfx = registry.get(graphicsEntity->entity); - const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance(); - for (const auto& renderableEntry : entityGfx.GetRenderables()) - m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask); + for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex) + { + const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex); + if (!renderableEntry.renderable) + continue; + + graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox()); + } } m_newlyVisibleGfxEntities.clear(); + + // Unregister lights for hidden entities + for (LightEntity* lightEntity : m_newlyHiddenLightEntities) + { + LightComponent& entityLights = registry.get(lightEntity->entity); + + for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex) + { + const auto& lightEntry = entityLights.GetLightEntry(lightIndex); + if (!lightEntry.light) + continue; + + m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]); + } + } + m_newlyHiddenGfxEntities.clear(); + + // Register lights for newly visible entities + for (LightEntity* lightEntity : m_newlyVisibleLightEntities) + { + LightComponent& entityLights = registry.get(lightEntity->entity); + + for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex) + { + const auto& lightEntry = entityLights.GetLightEntry(renderableIndex); + if (!lightEntry.light) + continue; + + lightEntity->lightIndices[renderableIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask); + } + } + m_newlyVisibleGfxEntities.clear(); + + //FIXME: Handle light visibility } }