Add frustum culling (WIP)

This commit is contained in:
Jérôme Leclercq 2021-08-19 23:26:34 +02:00
parent 8db8533300
commit 8546631f62
15 changed files with 643 additions and 463 deletions

View File

@ -211,11 +211,11 @@ int main()
}
planeMat->AddPass("ForwardPass", planeMatPass);
Nz::Model spaceshipModel(std::move(gfxMesh));
Nz::Model spaceshipModel(std::move(gfxMesh), spaceship->GetAABB());
for (std::size_t i = 0; i < spaceshipModel.GetSubMeshCount(); ++i)
spaceshipModel.SetMaterial(i, spaceshipMat);
Nz::Model planeModel(std::move(planeMeshGfx));
Nz::Model planeModel(std::move(planeMeshGfx), planeMesh->GetAABB());
for (std::size_t i = 0; i < planeModel.GetSubMeshCount(); ++i)
planeModel.SetMaterial(i, planeMat);

View File

@ -75,7 +75,7 @@ int main()
basicMat.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams));
basicMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "Spaceship/Texture/diffuse.png", texParams));
Nz::Model model(std::move(gfxMesh));
Nz::Model model(std::move(gfxMesh), spaceshipMesh->GetAABB());
for (std::size_t i = 0; i < model.GetSubMeshCount(); ++i)
model.SetMaterial(i, material);

View File

@ -61,6 +61,7 @@ int main()
return __LINE__;
}
const Nz::Boxf& spaceshipAABB = spaceshipMesh->GetAABB();
std::shared_ptr<Nz::GraphicalMesh> gfxMesh = std::make_shared<Nz::GraphicalMesh>(*spaceshipMesh);
// Texture
@ -102,7 +103,7 @@ int main()
Nz::DepthMaterial basicMatDepth(*depthPass);
basicMatDepth.SetAlphaMap(Nz::Texture::LoadFromFile(resourceDir / "alphatile.png", texParams));
std::shared_ptr<Nz::Model> model = std::make_shared<Nz::Model>(std::move(gfxMesh));
std::shared_ptr<Nz::Model> model = std::make_shared<Nz::Model>(std::move(gfxMesh), spaceshipAABB);
for (std::size_t i = 0; i < model->GetSubMeshCount(); ++i)
model->SetMaterial(i, material);
@ -140,7 +141,7 @@ int main()
std::shared_ptr<Nz::Mesh> colliderMesh = Nz::Mesh::Build(shipCollider->GenerateMesh());
std::shared_ptr<Nz::GraphicalMesh> colliderGraphicalMesh = std::make_shared<Nz::GraphicalMesh>(*colliderMesh);
colliderModel = std::make_shared<Nz::Model>(colliderGraphicalMesh);
colliderModel = std::make_shared<Nz::Model>(colliderGraphicalMesh, spaceshipAABB);
for (std::size_t i = 0; i < colliderModel->GetSubMeshCount(); ++i)
colliderModel->SetMaterial(i, colliderMat);
}
@ -168,11 +169,11 @@ int main()
registry.get<Nz::NodeComponent>(viewer).SetParent(registry, headingEntity);
registry.get<Nz::NodeComponent>(viewer).SetPosition(Nz::Vector3f::Backward() * 2.5f + Nz::Vector3f::Up() * 1.f);
for (std::size_t x = 0; x < 5; ++x)
for (std::size_t x = 0; x < 10; ++x)
{
for (std::size_t y = 0; y < 5; ++y)
for (std::size_t y = 0; y < 10; ++y)
{
for (std::size_t z = 0; z < 5; ++z)
for (std::size_t z = 0; z < 10; ++z)
{
entt::entity entity = registry.create();
auto& entityGfx = registry.emplace<Nz::GraphicsComponent>(entity);

View File

@ -54,6 +54,12 @@ namespace Nz
void ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue);
void UnregisterMaterialPass(MaterialPass* material);
struct ElementAABB
{
Boxf aabb;
std::size_t count;
};
struct MaterialData
{
std::size_t usedCount = 0;
@ -70,7 +76,19 @@ namespace Nz
{
std::size_t colorAttachment;
std::size_t depthStencilAttachment;
std::size_t depthPrepassVisibilityHash = 0;
std::size_t forwardVisibilityHash = 0;
std::vector<ElementAABB> depthPrepassAABB;
std::vector<ElementAABB> forwardAABB;
std::vector<std::unique_ptr<RenderElement>> depthPrepassRenderElements;
std::vector<std::unique_ptr<RenderElement>> forwardRenderElements;
RenderQueueRegistry depthPrepassRegistry;
RenderQueueRegistry forwardRegistry;
RenderQueue<RenderElement*> depthPrepassRenderQueue;
RenderQueue<RenderElement*> forwardRenderQueue;
ShaderBindingPtr blitShaderBinding;
bool rebuildDepthPrepass = true;
bool rebuildForwardPass = true;
};
std::size_t m_depthPassIndex;
@ -82,17 +100,9 @@ namespace Nz
std::unordered_set<MaterialPass*> m_invalidatedMaterials;
std::unordered_set<WorldInstance*> m_invalidatedWorldInstances;
std::unordered_set<WorldInstancePtr> m_removedWorldInstances;
std::vector<std::unique_ptr<RenderElement>> m_depthPrepassRenderElements;
std::vector<std::unique_ptr<RenderElement>> m_forwardRenderElements;
std::vector<std::unique_ptr<ElementRenderer>> m_elementRenderers;
BakedFrameGraph m_bakedFrameGraph;
RenderQueueRegistry m_depthPrepassRegistry;
RenderQueueRegistry m_forwardRegistry;
RenderQueue<RenderElement*> m_depthPrepassRenderQueue;
RenderQueue<RenderElement*> m_forwardRenderQueue;
bool m_rebuildDepthPrepass;
bool m_rebuildFrameGraph;
bool m_rebuildForwardPass;
};
}

View File

@ -10,6 +10,7 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Math/Box.hpp>
#include <memory>
namespace Nz
@ -22,13 +23,14 @@ namespace Nz
class NAZARA_GRAPHICS_API InstancedRenderable
{
public:
InstancedRenderable() = default;
inline InstancedRenderable(const Boxf& aabb);
InstancedRenderable(const InstancedRenderable&) = delete;
InstancedRenderable(InstancedRenderable&&) noexcept = default;
~InstancedRenderable();
virtual void BuildElement(std::size_t passIndex, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const = 0;
inline const Boxf& GetAABB() const;
virtual const std::shared_ptr<Material>& GetMaterial(std::size_t i) const = 0;
virtual std::size_t GetMaterialCount() const = 0;
@ -36,6 +38,9 @@ namespace Nz
InstancedRenderable& operator=(InstancedRenderable&&) noexcept = default;
NazaraSignal(OnMaterialInvalidated, InstancedRenderable* /*instancedRenderable*/, std::size_t /*materialIndex*/, const std::shared_ptr<Material>& /*newMaterial*/);
private:
Boxf m_aabb;
};
}

View File

@ -7,6 +7,15 @@
namespace Nz
{
inline InstancedRenderable::InstancedRenderable(const Boxf& aabb) :
m_aabb(aabb)
{
}
inline const Boxf& InstancedRenderable::GetAABB() const
{
return m_aabb;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -23,7 +23,7 @@ namespace Nz
class NAZARA_GRAPHICS_API Model : public InstancedRenderable
{
public:
Model(std::shared_ptr<GraphicalMesh> graphicalMesh);
Model(std::shared_ptr<GraphicalMesh> graphicalMesh, const Boxf& aabb);
Model(const Model&) = delete;
Model(Model&&) noexcept = default;
~Model() = default;

View File

@ -28,6 +28,12 @@ namespace Nz
ViewerInstance(ViewerInstance&&) noexcept = default;
~ViewerInstance() = default;
inline const Matrix4f& GetInvProjectionMatrix() const;
inline const Matrix4f& GetInvViewMatrix() const;
inline const Matrix4f& GetInvViewProjMatrix() const;
inline const Matrix4f& GetProjectionMatrix() const;
inline const Matrix4f& GetViewMatrix() const;
inline const Matrix4f& GetViewProjMatrix() const;
inline std::shared_ptr<AbstractBuffer>& GetInstanceBuffer();
inline const std::shared_ptr<AbstractBuffer>& GetInstanceBuffer() const;
inline ShaderBinding& GetShaderBinding();

View File

@ -8,6 +8,36 @@
namespace Nz
{
inline const Matrix4f& ViewerInstance::GetInvProjectionMatrix() const
{
return m_invProjectionMatrix;
}
inline const Matrix4f& ViewerInstance::GetInvViewMatrix() const
{
return m_invViewMatrix;
}
inline const Matrix4f& ViewerInstance::GetInvViewProjMatrix() const
{
return m_invViewProjMatrix;
}
inline const Matrix4f& ViewerInstance::GetProjectionMatrix() const
{
return m_projectionMatrix;
}
inline const Matrix4f& ViewerInstance::GetViewMatrix() const
{
return m_viewMatrix;
}
inline const Matrix4f& ViewerInstance::GetViewProjMatrix() const
{
return m_viewProjMatrix;
}
inline std::shared_ptr<AbstractBuffer>& ViewerInstance::GetInstanceBuffer()
{
return m_viewerDataBuffer;

View File

@ -33,8 +33,10 @@ namespace Nz
inline std::shared_ptr<AbstractBuffer>& GetInstanceBuffer();
inline const std::shared_ptr<AbstractBuffer>& GetInstanceBuffer() const;
inline const Matrix4f& GetInvWorldMatrix() const;
inline ShaderBinding& GetShaderBinding();
inline const ShaderBinding& GetShaderBinding() const;
inline const Matrix4f& GetWorldMatrix() const;
void UpdateBuffers(UploadPool& uploadPool, CommandBufferBuilder& builder);
inline void UpdateWorldMatrix(const Matrix4f& worldMatrix);

View File

@ -18,6 +18,11 @@ namespace Nz
return m_instanceDataBuffer;
}
inline const Matrix4f& WorldInstance::GetInvWorldMatrix() const
{
return m_invWorldMatrix;
}
inline ShaderBinding& WorldInstance::GetShaderBinding()
{
return *m_shaderBinding;
@ -28,6 +33,11 @@ namespace Nz
return *m_shaderBinding;
}
inline const Matrix4f& WorldInstance::GetWorldMatrix() const
{
return m_worldMatrix;
}
inline void WorldInstance::UpdateWorldMatrix(const Matrix4f& worldMatrix)
{
m_worldMatrix = worldMatrix;

View File

@ -264,7 +264,7 @@ namespace Nz
plane[2] *= invLength;
plane[3] *= -invLength;
m_planes[FrustumPlane::Right].Set(plane);
m_planes[UnderlyingCast(FrustumPlane::Right)].Set(plane);
// Extract the numbers for the LEFT plane
plane[0] = clipMatrix[ 3] + clipMatrix[ 0];
@ -279,7 +279,7 @@ namespace Nz
plane[2] *= invLength;
plane[3] *= -invLength;
m_planes[FrustumPlane::Left].Set(plane);
m_planes[UnderlyingCast(FrustumPlane::Left)].Set(plane);
// Extract the BOTTOM plane
plane[0] = clipMatrix[ 3] + clipMatrix[ 1];
@ -294,7 +294,7 @@ namespace Nz
plane[2] *= invLength;
plane[3] *= -invLength;
m_planes[FrustumPlane::Bottom].Set(plane);
m_planes[UnderlyingCast(FrustumPlane::Bottom)].Set(plane);
// Extract the TOP plane
plane[0] = clipMatrix[ 3] - clipMatrix[ 1];
@ -309,7 +309,7 @@ namespace Nz
plane[2] *= invLength;
plane[3] *= -invLength;
m_planes[FrustumPlane::Top].Set(plane);
m_planes[UnderlyingCast(FrustumPlane::Top)].Set(plane);
// Extract the FAR plane
plane[0] = clipMatrix[ 3] - clipMatrix[ 2];
@ -324,7 +324,7 @@ namespace Nz
plane[2] *= invLength;
plane[3] *= -invLength;
m_planes[FrustumPlane::Far].Set(plane);
m_planes[UnderlyingCast(FrustumPlane::Far)].Set(plane);
// Extract the NEAR plane
plane[0] = clipMatrix[ 3] + clipMatrix[ 2];
@ -339,7 +339,7 @@ namespace Nz
plane[2] *= invLength;
plane[3] *= -invLength;
m_planes[FrustumPlane::Near].Set(plane);
m_planes[UnderlyingCast(FrustumPlane::Near)].Set(plane);
// Once planes have been extracted, we must extract points of the frustum
// Based on: http://www.gamedev.net/topic/393309-calculating-the-view-frustums-vertices/
@ -354,56 +354,56 @@ namespace Nz
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::FarLeftBottom] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::FarLeftBottom)] = Vector3<T>(corner.x, corner.y, corner.z);
// FarLeftTop
corner.Set(T(-1.0), T(1.0), T(1.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::FarLeftTop] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::FarLeftTop)] = Vector3<T>(corner.x, corner.y, corner.z);
// FarRightBottom
corner.Set(T(1.0), T(-1.0), T(1.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::FarRightBottom] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::FarRightBottom)] = Vector3<T>(corner.x, corner.y, corner.z);
// FarRightTop
corner.Set(T(1.0), T(1.0), T(1.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::FarRightTop] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::FarRightTop)] = Vector3<T>(corner.x, corner.y, corner.z);
// NearLeftBottom
corner.Set(T(-1.0), T(-1.0), T(0.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::NearLeftBottom] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::NearLeftBottom)] = Vector3<T>(corner.x, corner.y, corner.z);
// NearLeftTop
corner.Set(T(-1.0), T(1.0), T(0.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::NearLeftTop] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::NearLeftTop)] = Vector3<T>(corner.x, corner.y, corner.z);
// NearRightBottom
corner.Set(T(1.0), T(-1.0), T(0.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::NearRightBottom] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::NearRightBottom)] = Vector3<T>(corner.x, corner.y, corner.z);
// NearRightTop
corner.Set(T(1.0), T(1.0), T(0.0));
corner = invClipMatrix.Transform(corner);
corner.Normalize();
m_corners[BoxCorner::NearRightTop] = Vector3<T>(corner.x, corner.y, corner.z);
m_corners[UnderlyingCast(BoxCorner::NearRightTop)] = Vector3<T>(corner.x, corner.y, corner.z);
}
else
NazaraWarning("Clip matrix is not invertible, failed to compute frustum corners");
@ -440,9 +440,9 @@ namespace Nz
const Vector3<T>& Frustum<T>::GetCorner(BoxCorner corner) const
{
#ifdef NAZARA_DEBUG
if (corner > BoxCornerCount)
if (UnderlyingCast(corner) > BoxCornerCount)
{
NazaraError("Corner not handled (0x" + NumberToString(corner, 16) + ')');
NazaraError("Corner not handled (0x" + NumberToString(UnderlyingCast(corner), 16) + ')');
static Vector3<T> dummy;
return dummy;
@ -465,9 +465,9 @@ namespace Nz
const Plane<T>& Frustum<T>::GetPlane(FrustumPlane plane) const
{
#ifdef NAZARA_DEBUG
if (plane > FrustumPlane::Max)
if (UnderlyingCast(plane) > FrustumPlaneCount)
{
NazaraError("Frustum plane not handled (0x" + NumberToString(plane, 16) + ')');
NazaraError("Frustum plane not handled (0x" + NumberToString(UnderlyingCast(plane), 16) + ')');
static Plane<T> dummy;
return dummy;

View File

@ -89,9 +89,9 @@ namespace Nz
const Vector3<T>& OrientedBox<T>::GetCorner(BoxCorner corner) const
{
#ifdef NAZARA_DEBUG
if (corner > BoxCornerCount)
if (UnderlyingCast(corner) > BoxCornerCount)
{
NazaraError("Corner not handled (0x" + NumberToString(corner, 16) + ')');
NazaraError("Corner not handled (0x" + NumberToString(UnderlyingCast(corner), 16) + ')');
static Vector3<T> dummy;
return dummy;

View File

@ -1,442 +1,548 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/ForwardFramePipeline.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Graphics/SubmeshRenderer.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp>
#include <Nazara/Graphics/WorldInstance.hpp>
#include <Nazara/Math/Frustum.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/Framebuffer.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Renderer/UploadPool.hpp>
#include <array>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
ForwardFramePipeline::ForwardFramePipeline() :
m_rebuildFrameGraph(true)
{
auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry();
m_depthPassIndex = passRegistry.GetPassIndex("DepthPass");
m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass");
m_elementRenderers.resize(1);
m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique<SubmeshRenderer>();
}
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
{
m_invalidatedViewerInstances.insert(viewerInstance);
}
void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance)
{
m_invalidatedWorldInstances.insert(worldInstance);
}
void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable)
{
m_removedWorldInstances.erase(worldInstance);
auto& renderableMap = m_renderables[worldInstance];
if (renderableMap.empty())
InvalidateWorldInstance(worldInstance.get());
if (auto it = renderableMap.find(instancedRenderable); it == renderableMap.end())
{
auto& renderableData = renderableMap.emplace(instancedRenderable, RenderableData{}).first->second;
renderableData.onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr<Material>& newMaterial)
{
if (newMaterial)
{
if (MaterialPass* pass = newMaterial->GetPass(m_depthPassIndex))
RegisterMaterialPass(pass);
if (MaterialPass* pass = newMaterial->GetPass(m_forwardPassIndex))
RegisterMaterialPass(pass);
}
const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex);
if (prevMaterial)
{
if (MaterialPass* pass = prevMaterial->GetPass(m_depthPassIndex))
UnregisterMaterialPass(pass);
if (MaterialPass* pass = prevMaterial->GetPass(m_forwardPassIndex))
UnregisterMaterialPass(pass);
}
#include <Nazara/Graphics/ForwardFramePipeline.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Graphics/SubmeshRenderer.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp>
#include <Nazara/Graphics/WorldInstance.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/Framebuffer.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Renderer/UploadPool.hpp>
#include <array>
#include <Nazara/Graphics/Debug.hpp>
for (auto&& [viewer, viewerData] : m_viewers)
{
viewerData.rebuildDepthPrepass = true;
viewerData.rebuildForwardPass = true;
}
});
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (Material* mat = instancedRenderable->GetMaterial(i).get())
{
if (MaterialPass* pass = mat->GetPass(m_depthPassIndex))
RegisterMaterialPass(pass);
if (MaterialPass* pass = mat->GetPass(m_forwardPassIndex))
RegisterMaterialPass(pass);
}
}
namespace Nz
{
ForwardFramePipeline::ForwardFramePipeline() :
m_rebuildFrameGraph(true),
m_rebuildDepthPrepass(false),
m_rebuildForwardPass(false)
{
auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry();
m_depthPassIndex = passRegistry.GetPassIndex("DepthPass");
m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass");
m_elementRenderers.resize(1);
m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique<SubmeshRenderer>();
}
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
{
for (auto&& [viewer, viewerData] : m_viewers)
{
viewerData.rebuildDepthPrepass = true;
viewerData.rebuildForwardPass = true;
}
}
}
void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance)
{
m_viewers.emplace(viewerInstance, ViewerData{});
m_invalidatedViewerInstances.insert(viewerInstance);
}
void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance)
{
m_invalidatedWorldInstances.insert(worldInstance);
}
void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable)
{
m_removedWorldInstances.erase(worldInstance);
auto& renderableMap = m_renderables[worldInstance];
if (renderableMap.empty())
InvalidateWorldInstance(worldInstance.get());
if (auto it = renderableMap.find(instancedRenderable); it == renderableMap.end())
{
auto& renderableData = renderableMap.emplace(instancedRenderable, RenderableData{}).first->second;
renderableData.onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr<Material>& newMaterial)
{
if (newMaterial)
{
if (MaterialPass* pass = newMaterial->GetPass(m_depthPassIndex))
RegisterMaterialPass(pass);
if (MaterialPass* pass = newMaterial->GetPass(m_forwardPassIndex))
RegisterMaterialPass(pass);
}
const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex);
if (prevMaterial)
{
if (MaterialPass* pass = prevMaterial->GetPass(m_depthPassIndex))
UnregisterMaterialPass(pass);
if (MaterialPass* pass = prevMaterial->GetPass(m_forwardPassIndex))
UnregisterMaterialPass(pass);
}
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
});
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (Material* mat = instancedRenderable->GetMaterial(i).get())
{
if (MaterialPass* pass = mat->GetPass(m_depthPassIndex))
RegisterMaterialPass(pass);
if (MaterialPass* pass = mat->GetPass(m_forwardPassIndex))
RegisterMaterialPass(pass);
}
}
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
}
}
void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance)
{
m_viewers.emplace(viewerInstance, ViewerData{});
m_invalidatedViewerInstances.insert(viewerInstance);
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
{
Graphics* graphics = Graphics::Instance();
renderFrame.PushForRelease(std::move(m_removedWorldInstances));
m_removedWorldInstances.clear();
if (m_rebuildFrameGraph)
{
renderFrame.PushForRelease(std::move(m_bakedFrameGraph));
m_bakedFrameGraph = BuildFrameGraph();
}
// Update UBOs and materials
UploadPool& uploadPool = renderFrame.GetUploadPool();
renderFrame.Execute([&](CommandBufferBuilder& builder)
{
builder.BeginDebugRegion("UBO Update", Color::Yellow);
{
builder.PreTransferBarrier();
for (AbstractViewer* viewer : m_invalidatedViewerInstances)
viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder);
m_invalidatedViewerInstances.clear();
for (WorldInstance* worldInstance : m_invalidatedWorldInstances)
worldInstance->UpdateBuffers(uploadPool, builder);
m_invalidatedWorldInstances.clear();
for (MaterialPass* material : m_invalidatedMaterials)
{
if (material->Update(renderFrame, builder))
m_rebuildFrameGraph = true;
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
{
Graphics* graphics = Graphics::Instance();
renderFrame.PushForRelease(std::move(m_removedWorldInstances));
m_removedWorldInstances.clear();
if (m_rebuildFrameGraph)
{
renderFrame.PushForRelease(std::move(m_bakedFrameGraph));
m_bakedFrameGraph = BuildFrameGraph();
}
// Update UBOs and materials
UploadPool& uploadPool = renderFrame.GetUploadPool();
renderFrame.Execute([&](CommandBufferBuilder& builder)
{
builder.BeginDebugRegion("UBO Update", Color::Yellow);
{
builder.PreTransferBarrier();
for (AbstractViewer* viewer : m_invalidatedViewerInstances)
viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder);
m_invalidatedViewerInstances.clear();
for (WorldInstance* worldInstance : m_invalidatedWorldInstances)
worldInstance->UpdateBuffers(uploadPool, builder);
m_invalidatedWorldInstances.clear();
for (MaterialPass* material : m_invalidatedMaterials)
{
if (material->Update(renderFrame, builder))
{
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
}
}
m_invalidatedMaterials.clear();
for (auto&& [viewer, viewerData] : m_viewers)
{
viewerData.rebuildDepthPrepass = true;
viewerData.rebuildForwardPass = true;
}
}
}
m_invalidatedMaterials.clear();
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, QueueType::Transfer);
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, QueueType::Transfer);
if (m_rebuildDepthPrepass)
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
{
m_depthPrepassRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
renderable->BuildElement(m_depthPassIndex, *worldInstance, m_depthPrepassRenderElements);
}
}
if (m_rebuildForwardPass)
return currentHash * 23 + newHash;
};
// Render queues handling
for (auto&& [viewer, data] : m_viewers)
{
m_forwardRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
renderable->BuildElement(m_forwardPassIndex, *worldInstance, m_forwardRenderElements);
}
}
// RenderQueue handling
m_depthPrepassRegistry.Clear();
m_depthPrepassRenderQueue.Clear();
for (const auto& renderElement : m_depthPrepassRenderElements)
{
renderElement->Register(m_depthPrepassRegistry);
m_depthPrepassRenderQueue.Insert(renderElement.get());
}
m_depthPrepassRenderQueue.Sort([&](const RenderElement* element)
{
return element->ComputeSortingScore(m_depthPrepassRegistry);
});
m_forwardRegistry.Clear();
m_forwardRenderQueue.Clear();
for (const auto& renderElement : m_forwardRenderElements)
{
renderElement->Register(m_forwardRegistry);
m_forwardRenderQueue.Insert(renderElement.get());
}
m_forwardRenderQueue.Sort([&](const RenderElement* element)
{
return element->ComputeSortingScore(m_forwardRegistry);
});
if (m_bakedFrameGraph.Resize(renderFrame))
{
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
for (auto&& [_, viewerData] : m_viewers)
{
if (viewerData.blitShaderBinding)
renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding));
viewerData.blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0);
viewerData.blitShaderBinding->Update({
auto& viewerData = data;
//if (viewerData.rebuildDepthPrepass)
{
viewerData.depthPrepassAABB.clear();
viewerData.depthPrepassRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
{
0,
ShaderBinding::TextureBinding {
m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment).get(),
sampler.get()
}
std::size_t prevCount = viewerData.depthPrepassRenderElements.size();
renderable->BuildElement(m_depthPassIndex, *worldInstance, viewerData.depthPrepassRenderElements);
std::size_t currentCount = viewerData.depthPrepassRenderElements.size();
if (currentCount > prevCount)
{
BoundingVolumef boundingVolume(renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
auto& aabbData = viewerData.depthPrepassAABB.emplace_back();
aabbData.aabb = boundingVolume.aabb;
aabbData.count = currentCount - prevCount;
}
}
}
}
//if (viewerData.rebuildForwardPass)
{
viewerData.forwardAABB.clear();
viewerData.forwardRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
{
std::size_t prevCount = viewerData.forwardRenderElements.size();
renderable->BuildElement(m_forwardPassIndex, *worldInstance, viewerData.forwardRenderElements);
std::size_t currentCount = viewerData.forwardRenderElements.size();
if (currentCount > prevCount)
{
BoundingVolumef boundingVolume(renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
auto& aabbData = viewerData.forwardAABB.emplace_back();
aabbData.aabb = boundingVolume.aabb;
aabbData.count = currentCount - prevCount;
}
}
}
}
const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix();
Frustumf frustum;
frustum.Extract(viewProjMatrix);
viewerData.depthPrepassRegistry.Clear();
viewerData.depthPrepassRenderQueue.Clear();
{
std::size_t visibilityHash = 5U;
auto elementIt = viewerData.depthPrepassRenderElements.begin();
for (auto&& [aabb, elementCount] : viewerData.depthPrepassAABB)
{
if (!frustum.Contains(aabb))
{
std::advance(elementIt, elementCount);
continue;
}
for (std::size_t i = 0; i < elementCount; ++i)
{
auto& renderElement = *elementIt++;
renderElement->Register(viewerData.depthPrepassRegistry);
viewerData.depthPrepassRenderQueue.Insert(renderElement.get());
visibilityHash = CombineHash(visibilityHash, std::hash<const RenderElement*>()(renderElement.get()));
}
}
viewerData.depthPrepassRenderQueue.Sort([&](const RenderElement* element)
{
return element->ComputeSortingScore(viewerData.depthPrepassRegistry);
});
}
}
m_bakedFrameGraph.Execute(renderFrame);
m_rebuildForwardPass = false;
m_rebuildDepthPrepass = false;
m_rebuildFrameGraph = false;
const Vector2ui& frameSize = renderFrame.GetSize();
for (auto&& [viewer, viewerData] : m_viewers)
{
const RenderTarget& renderTarget = viewer->GetRenderTarget();
Recti renderRegion(0, 0, frameSize.x, frameSize.y);
const ShaderBindingPtr& blitShaderBinding = viewerData.blitShaderBinding;
const std::shared_ptr<Texture>& sourceTexture = m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment);
renderFrame.Execute([&](CommandBufferBuilder& builder)
{
builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::FragmentShader, MemoryAccess::ColorWrite, MemoryAccess::ShaderRead, TextureLayout::ColorOutput, TextureLayout::ColorInput, *sourceTexture);
std::array<CommandBufferBuilder::ClearValues, 2> clearValues;
clearValues[0].color = Color::Black;
clearValues[1].depth = 1.f;
clearValues[1].stencil = 0;
builder.BeginDebugRegion("Main window rendering", Color::Green);
if (viewerData.depthPrepassVisibilityHash != visibilityHash)
{
builder.BeginRenderPass(renderTarget.GetFramebuffer(renderFrame.GetFramebufferIndex()), renderTarget.GetRenderPass(), renderRegion, { clearValues[0], clearValues[1] });
{
builder.SetScissor(renderRegion);
builder.SetViewport(renderRegion);
builder.BindPipeline(*graphics->GetBlitPipeline());
builder.BindVertexBuffer(0, *graphics->GetFullscreenVertexBuffer());
builder.BindShaderBinding(0, *blitShaderBinding);
builder.Draw(3);
}
builder.EndRenderPass();
viewerData.rebuildDepthPrepass = true;
viewerData.depthPrepassVisibilityHash = visibilityHash;
}
builder.EndDebugRegion();
}, QueueType::Graphics);
}
}
void ForwardFramePipeline::UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable)
{
auto instanceIt = m_renderables.find(worldInstance);
if (instanceIt == m_renderables.end())
return;
auto& instancedRenderables = instanceIt->second;
auto renderableIt = instancedRenderables.find(instancedRenderable);
if (renderableIt == instancedRenderables.end())
return;
if (instancedRenderables.size() > 1)
instancedRenderables.erase(renderableIt);
else
{
m_removedWorldInstances.insert(worldInstance);
m_renderables.erase(instanceIt);;
}
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_depthPassIndex))
UnregisterMaterialPass(pass);
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_forwardPassIndex))
UnregisterMaterialPass(pass);
}
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
}
void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance)
{
m_viewers.erase(viewerInstance);
m_rebuildFrameGraph = true;
}
BakedFrameGraph ForwardFramePipeline::BuildFrameGraph()
{
FrameGraph frameGraph;
for (auto&& [viewer, viewerData] : m_viewers)
{
viewerData.colorAttachment = frameGraph.AddAttachment({
"Color",
PixelFormat::RGBA8
});
viewerData.depthStencilAttachment = frameGraph.AddAttachment({
"Depth-stencil buffer",
Graphics::Instance()->GetPreferredDepthStencilFormat()
});
}
for (auto&& [viewer, viewerData] : m_viewers)
{
FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass");
depthPrepass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
depthPrepass.SetDepthStencilClear(1.f, 0);
depthPrepass.SetExecutionCallback([this]()
}
viewerData.forwardRegistry.Clear();
viewerData.forwardRenderQueue.Clear();
{
if (m_rebuildForwardPass)
return FramePassExecution::UpdateAndExecute;
else
return FramePassExecution::Execute;
});
depthPrepass.SetCommandCallback([this, viewer = viewer](CommandBufferBuilder& builder, const Recti& /*renderRect*/)
{
Recti viewport = viewer->GetViewport();
std::size_t visibilityHash = 5U;
auto elementIt = viewerData.forwardRenderElements.begin();
for (auto&& [aabb, elementCount] : viewerData.forwardAABB)
{
if (!frustum.Contains(aabb))
{
std::advance(elementIt, elementCount);
continue;
}
for (std::size_t i = 0; i < elementCount; ++i)
{
auto& renderElement = *elementIt++;
renderElement->Register(viewerData.forwardRegistry);
viewerData.forwardRenderQueue.Insert(renderElement.get());
builder.SetScissor(viewport);
builder.SetViewport(viewport);
visibilityHash = CombineHash(visibilityHash, std::hash<const RenderElement*>()(renderElement.get()));
}
}
viewerData.forwardRenderQueue.Sort([&](const RenderElement* element)
{
return element->ComputeSortingScore(viewerData.forwardRegistry);
});
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
ProcessRenderQueue(builder, m_depthPrepassRenderQueue);
});
FramePass& forwardPass = frameGraph.AddPass("Forward pass");
forwardPass.AddOutput(viewerData.colorAttachment);
forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment);
//forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
forwardPass.SetClearColor(0, Color::Black);
forwardPass.SetDepthStencilClear(1.f, 0);
forwardPass.SetExecutionCallback([this]()
{
if (m_rebuildForwardPass)
return FramePassExecution::UpdateAndExecute;
else
return FramePassExecution::Execute;
});
forwardPass.SetCommandCallback([this, viewer = viewer](CommandBufferBuilder& builder, const Recti& /*renderRect*/)
{
Recti viewport = viewer->GetViewport();
builder.SetScissor(viewport);
builder.SetViewport(viewport);
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
ProcessRenderQueue(builder, m_forwardRenderQueue);
});
}
//FIXME: This doesn't handle multiple window viewers
for (auto&& [viewer, viewerData] : m_viewers)
frameGraph.SetBackbufferOutput(viewerData.colorAttachment);
return frameGraph.Bake();
}
void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* material)
{
auto it = m_materials.find(material);
if (it == m_materials.end())
if (viewerData.forwardVisibilityHash != visibilityHash)
{
viewerData.rebuildForwardPass = true;
viewerData.forwardVisibilityHash = visibilityHash;
}
}
}
if (m_bakedFrameGraph.Resize(renderFrame))
{
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
for (auto&& [_, viewerData] : m_viewers)
{
if (viewerData.blitShaderBinding)
renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding));
viewerData.blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0);
viewerData.blitShaderBinding->Update({
{
0,
ShaderBinding::TextureBinding {
m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment).get(),
sampler.get()
}
}
});
}
}
m_bakedFrameGraph.Execute(renderFrame);
m_rebuildFrameGraph = false;
const Vector2ui& frameSize = renderFrame.GetSize();
for (auto&& [viewer, viewerData] : m_viewers)
{
it = m_materials.emplace(material, MaterialData{}).first;
it->second.onMaterialInvalided.Connect(material->OnMaterialInvalidated, [this, material](const MaterialPass* /*material*/)
{
m_invalidatedMaterials.insert(material);
});
m_invalidatedMaterials.insert(material);
}
it->second.usedCount++;
}
void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue)
{
if (renderQueue.empty())
return;
auto it = renderQueue.begin();
auto itEnd = renderQueue.end();
while (it != itEnd)
viewerData.rebuildForwardPass = false;
viewerData.rebuildDepthPrepass = false;
const RenderTarget& renderTarget = viewer->GetRenderTarget();
Recti renderRegion(0, 0, frameSize.x, frameSize.y);
const ShaderBindingPtr& blitShaderBinding = viewerData.blitShaderBinding;
const std::shared_ptr<Texture>& sourceTexture = m_bakedFrameGraph.GetAttachmentTexture(viewerData.colorAttachment);
renderFrame.Execute([&](CommandBufferBuilder& builder)
{
builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::FragmentShader, MemoryAccess::ColorWrite, MemoryAccess::ShaderRead, TextureLayout::ColorOutput, TextureLayout::ColorInput, *sourceTexture);
std::array<CommandBufferBuilder::ClearValues, 2> clearValues;
clearValues[0].color = Color::Black;
clearValues[1].depth = 1.f;
clearValues[1].stencil = 0;
builder.BeginDebugRegion("Main window rendering", Color::Green);
{
builder.BeginRenderPass(renderTarget.GetFramebuffer(renderFrame.GetFramebufferIndex()), renderTarget.GetRenderPass(), renderRegion, { clearValues[0], clearValues[1] });
{
builder.SetScissor(renderRegion);
builder.SetViewport(renderRegion);
builder.BindPipeline(*graphics->GetBlitPipeline());
builder.BindVertexBuffer(0, *graphics->GetFullscreenVertexBuffer());
builder.BindShaderBinding(0, *blitShaderBinding);
builder.Draw(3);
}
builder.EndRenderPass();
}
builder.EndDebugRegion();
}, QueueType::Graphics);
}
}
void ForwardFramePipeline::UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable)
{
auto instanceIt = m_renderables.find(worldInstance);
if (instanceIt == m_renderables.end())
return;
auto& instancedRenderables = instanceIt->second;
auto renderableIt = instancedRenderables.find(instancedRenderable);
if (renderableIt == instancedRenderables.end())
return;
if (instancedRenderables.size() > 1)
instancedRenderables.erase(renderableIt);
else
{
m_removedWorldInstances.insert(worldInstance);
m_renderables.erase(instanceIt);;
}
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_depthPassIndex))
UnregisterMaterialPass(pass);
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_forwardPassIndex))
UnregisterMaterialPass(pass);
}
for (auto&& [viewer, viewerData] : m_viewers)
{
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
const Pointer<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;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, first, count);
}
}
void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* material)
{
auto it = m_materials.find(material);
assert(it != m_materials.end());
MaterialData& materialData = it->second;
assert(materialData.usedCount > 0);
if (--materialData.usedCount == 0)
m_materials.erase(material);
}
}
viewerData.rebuildDepthPrepass = true;
viewerData.rebuildForwardPass = true;
}
}
void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance)
{
m_viewers.erase(viewerInstance);
m_rebuildFrameGraph = true;
}
BakedFrameGraph ForwardFramePipeline::BuildFrameGraph()
{
FrameGraph frameGraph;
for (auto&& [viewer, viewerData] : m_viewers)
{
viewerData.colorAttachment = frameGraph.AddAttachment({
"Color",
PixelFormat::RGBA8
});
viewerData.depthStencilAttachment = frameGraph.AddAttachment({
"Depth-stencil buffer",
Graphics::Instance()->GetPreferredDepthStencilFormat()
});
}
for (auto&& [viewer, data] : m_viewers)
{
auto& viewerData = data;
FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass");
depthPrepass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
depthPrepass.SetDepthStencilClear(1.f, 0);
depthPrepass.SetExecutionCallback([&]()
{
if (viewerData.rebuildDepthPrepass)
return FramePassExecution::UpdateAndExecute;
else
return FramePassExecution::Execute;
});
depthPrepass.SetCommandCallback([this, viewer = viewer, &viewerData](CommandBufferBuilder& builder, const Recti& /*renderRect*/)
{
Recti viewport = viewer->GetViewport();
builder.SetScissor(viewport);
builder.SetViewport(viewport);
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
ProcessRenderQueue(builder, viewerData.depthPrepassRenderQueue);
});
FramePass& forwardPass = frameGraph.AddPass("Forward pass");
forwardPass.AddOutput(viewerData.colorAttachment);
forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment);
//forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
forwardPass.SetClearColor(0, Color::Black);
forwardPass.SetDepthStencilClear(1.f, 0);
forwardPass.SetExecutionCallback([&]()
{
if (viewerData.rebuildForwardPass)
return FramePassExecution::UpdateAndExecute;
else
return FramePassExecution::Execute;
});
forwardPass.SetCommandCallback([this, viewer = viewer, &viewerData](CommandBufferBuilder& builder, const Recti& /*renderRect*/)
{
Recti viewport = viewer->GetViewport();
builder.SetScissor(viewport);
builder.SetViewport(viewport);
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
ProcessRenderQueue(builder, viewerData.forwardRenderQueue);
});
}
//FIXME: This doesn't handle multiple window viewers
for (auto&& [viewer, viewerData] : m_viewers)
frameGraph.SetBackbufferOutput(viewerData.colorAttachment);
return frameGraph.Bake();
}
void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* material)
{
auto it = m_materials.find(material);
if (it == m_materials.end())
{
it = m_materials.emplace(material, MaterialData{}).first;
it->second.onMaterialInvalided.Connect(material->OnMaterialInvalidated, [this, material](const MaterialPass* /*material*/)
{
m_invalidatedMaterials.insert(material);
});
m_invalidatedMaterials.insert(material);
}
it->second.usedCount++;
}
void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue)
{
if (renderQueue.empty())
return;
auto it = renderQueue.begin();
auto itEnd = renderQueue.end();
while (it != itEnd)
{
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
const Pointer<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;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, first, count);
}
}
void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* material)
{
auto it = m_materials.find(material);
assert(it != m_materials.end());
MaterialData& materialData = it->second;
assert(materialData.usedCount > 0);
if (--materialData.usedCount == 0)
m_materials.erase(material);
}
}

View File

@ -13,7 +13,8 @@
namespace Nz
{
Model::Model(std::shared_ptr<GraphicalMesh> graphicalMesh) :
Model::Model(std::shared_ptr<GraphicalMesh> graphicalMesh, const Boxf& aabb) :
InstancedRenderable(aabb),
m_graphicalMesh(std::move(graphicalMesh))
{
m_submeshes.reserve(m_graphicalMesh->GetSubMeshCount());