Graphics: Improve frustum culling (do it once per viewer)

This commit is contained in:
Jérôme Leclercq 2021-08-21 01:42:53 +02:00
parent 8546631f62
commit 0179ef4d65
5 changed files with 478 additions and 524 deletions

View File

@ -54,12 +54,6 @@ namespace Nz
void ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue); void ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue);
void UnregisterMaterialPass(MaterialPass* material); void UnregisterMaterialPass(MaterialPass* material);
struct ElementAABB
{
Boxf aabb;
std::size_t count;
};
struct MaterialData struct MaterialData
{ {
std::size_t usedCount = 0; std::size_t usedCount = 0;
@ -72,14 +66,17 @@ namespace Nz
NazaraSlot(InstancedRenderable, OnMaterialInvalidated, onMaterialInvalidated); NazaraSlot(InstancedRenderable, OnMaterialInvalidated, onMaterialInvalidated);
}; };
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 depthPrepassVisibilityHash = 0; std::size_t visibilityHash = 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>> depthPrepassRenderElements;
std::vector<std::unique_ptr<RenderElement>> forwardRenderElements; std::vector<std::unique_ptr<RenderElement>> forwardRenderElements;
RenderQueueRegistry depthPrepassRegistry; RenderQueueRegistry depthPrepassRegistry;
@ -101,6 +98,7 @@ namespace Nz
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<std::unique_ptr<ElementRenderer>> m_elementRenderers;
std::vector<VisibleRenderable> m_visibleRenderables;
BakedFrameGraph m_bakedFrameGraph; BakedFrameGraph m_bakedFrameGraph;
bool m_rebuildFrameGraph; bool m_rebuildFrameGraph;
}; };

View File

@ -28,7 +28,7 @@ namespace Nz
InstancedRenderable(InstancedRenderable&&) noexcept = default; InstancedRenderable(InstancedRenderable&&) noexcept = default;
~InstancedRenderable(); ~InstancedRenderable();
virtual void BuildElement(std::size_t passIndex, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const = 0; virtual void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const = 0;
inline const Boxf& GetAABB() const; inline const Boxf& GetAABB() const;
virtual const std::shared_ptr<Material>& GetMaterial(std::size_t i) const = 0; virtual const std::shared_ptr<Material>& GetMaterial(std::size_t i) const = 0;

View File

@ -28,7 +28,7 @@ namespace Nz
Model(Model&&) noexcept = default; Model(Model&&) noexcept = default;
~Model() = default; ~Model() = default;
void BuildElement(std::size_t passIndex, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const override; void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const override;
const std::shared_ptr<AbstractBuffer>& GetIndexBuffer(std::size_t subMeshIndex) const; const std::shared_ptr<AbstractBuffer>& GetIndexBuffer(std::size_t subMeshIndex) const;
std::size_t GetIndexCount(std::size_t subMeshIndex) const; std::size_t GetIndexCount(std::size_t subMeshIndex) const;

View File

@ -171,134 +171,90 @@ namespace Nz
{ {
auto& viewerData = data; auto& viewerData = data;
//if (viewerData.rebuildDepthPrepass) // Frustum culling
{
viewerData.depthPrepassAABB.clear();
viewerData.depthPrepassRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
{
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(); const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix();
Frustumf frustum; Frustumf frustum;
frustum.Extract(viewProjMatrix); frustum.Extract(viewProjMatrix);
viewerData.depthPrepassRegistry.Clear(); std::size_t visibilityHash = 5U;
viewerData.depthPrepassRenderQueue.Clear();
m_visibleRenderables.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{ {
std::size_t visibilityHash = 5U; bool isInstanceVisible = false;
auto elementIt = viewerData.depthPrepassRenderElements.begin(); for (const auto& [renderable, renderableData] : renderables)
for (auto&& [aabb, elementCount] : viewerData.depthPrepassAABB)
{ {
if (!frustum.Contains(aabb)) // Get global AABB
{ BoundingVolumef boundingVolume(renderable->GetAABB());
std::advance(elementIt, elementCount); boundingVolume.Update(worldInstance->GetWorldMatrix());
if (!frustum.Contains(boundingVolume.aabb))
continue; continue;
}
for (std::size_t i = 0; i < elementCount; ++i) auto& renderableData = m_visibleRenderables.emplace_back();
{ renderableData.instancedRenderable = renderable;
auto& renderElement = *elementIt++; renderableData.worldInstance = worldInstance.get();
renderElement->Register(viewerData.depthPrepassRegistry);
viewerData.depthPrepassRenderQueue.Insert(renderElement.get());
visibilityHash = CombineHash(visibilityHash, std::hash<const RenderElement*>()(renderElement.get())); isInstanceVisible = true;
} visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(renderable));
} }
viewerData.depthPrepassRenderQueue.Sort([&](const RenderElement* element) if (isInstanceVisible)
{ visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(worldInstance.get()));
return element->ComputeSortingScore(viewerData.depthPrepassRegistry); }
});
if (viewerData.depthPrepassVisibilityHash != visibilityHash) if (viewerData.visibilityHash != visibilityHash)
{
viewerData.rebuildDepthPrepass = true;
viewerData.rebuildForwardPass = true;
viewerData.visibilityHash = visibilityHash;
}
if (viewerData.rebuildDepthPrepass)
{
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)
{ {
viewerData.rebuildDepthPrepass = true; renderElement->Register(viewerData.depthPrepassRegistry);
viewerData.depthPrepassVisibilityHash = visibilityHash; viewerData.depthPrepassRenderQueue.Insert(renderElement.get());
} }
} }
viewerData.forwardRegistry.Clear(); viewerData.depthPrepassRenderQueue.Sort([&](const RenderElement* element)
viewerData.forwardRenderQueue.Clear();
{ {
std::size_t visibilityHash = 5U; return element->ComputeSortingScore(viewerData.depthPrepassRegistry);
});
auto elementIt = viewerData.forwardRenderElements.begin(); if (viewerData.rebuildForwardPass)
for (auto&& [aabb, elementCount] : viewerData.forwardAABB) {
viewerData.forwardRenderElements.clear();
for (const auto& renderableData : m_visibleRenderables)
renderableData.instancedRenderable->BuildElement(m_forwardPassIndex, *renderableData.worldInstance, viewerData.forwardRenderElements);
viewerData.forwardRegistry.Clear();
viewerData.forwardRenderQueue.Clear();
for (const auto& renderElement : viewerData.forwardRenderElements)
{ {
if (!frustum.Contains(aabb)) renderElement->Register(viewerData.forwardRegistry);
{ viewerData.forwardRenderQueue.Insert(renderElement.get());
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());
visibilityHash = CombineHash(visibilityHash, std::hash<const RenderElement*>()(renderElement.get()));
}
}
viewerData.forwardRenderQueue.Sort([&](const RenderElement* element)
{
return element->ComputeSortingScore(viewerData.forwardRegistry);
});
if (viewerData.forwardVisibilityHash != visibilityHash)
{
viewerData.rebuildForwardPass = true;
viewerData.forwardVisibilityHash = visibilityHash;
} }
} }
viewerData.forwardRenderQueue.Sort([&](const RenderElement* element)
{
return element->ComputeSortingScore(viewerData.forwardRegistry);
});
} }
if (m_bakedFrameGraph.Resize(renderFrame)) if (m_bakedFrameGraph.Resize(renderFrame))

View File

@ -31,7 +31,7 @@ namespace Nz
} }
} }
void Model::BuildElement(std::size_t passIndex, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const void Model::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const
{ {
for (std::size_t i = 0; i < m_submeshes.size(); ++i) for (std::size_t i = 0; i < m_submeshes.size(); ++i)
{ {