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