Graphics: Add RenderElement and ElementRenderer (WIP)

This commit is contained in:
Jérôme Leclercq
2021-07-25 21:13:24 +02:00
parent d647fdc59b
commit 08921b36a6
27 changed files with 3239 additions and 38 deletions

View File

@@ -0,0 +1,11 @@
// 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/ElementRenderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
ElementRenderer::~ElementRenderer() = default;
}

View File

@@ -6,8 +6,10 @@
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/Material.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>
@@ -22,8 +24,11 @@ namespace Nz
{
ForwardFramePipeline::ForwardFramePipeline() :
m_rebuildFrameGraph(true),
m_rebuildDepthPrepass(false),
m_rebuildForwardPass(false)
{
m_elementRenderers.resize(1);
m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique<SubmeshRenderer>();
}
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
@@ -63,6 +68,7 @@ namespace Nz
UnregisterMaterialPass(pass);
}
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
});
@@ -79,6 +85,7 @@ namespace Nz
}
}
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
}
}
@@ -120,7 +127,10 @@ namespace Nz
for (MaterialPass* material : m_invalidatedMaterials)
{
if (material->Update(renderFrame, builder))
{
m_rebuildDepthPrepass = true;
m_rebuildForwardPass = true;
}
}
m_invalidatedMaterials.clear();
@@ -129,6 +139,55 @@ namespace Nz
builder.EndDebugRegion();
}, QueueType::Transfer);
if (m_rebuildDepthPrepass)
{
m_depthPrepassRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
renderable->BuildElement("DepthPass", *worldInstance, m_depthPrepassRenderElements);
}
}
if (m_rebuildForwardPass)
{
m_forwardRenderElements.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
renderable->BuildElement("ForwardPass", *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({});
@@ -152,6 +211,7 @@ namespace Nz
m_bakedFrameGraph.Execute(renderFrame);
m_rebuildForwardPass = false;
m_rebuildDepthPrepass = false;
m_rebuildFrameGraph = false;
const Vector2ui& frameSize = renderFrame.GetSize();
@@ -179,7 +239,7 @@ namespace Nz
builder.SetViewport(renderRegion);
builder.BindPipeline(*graphics->GetBlitPipeline());
builder.BindVertexBuffer(0, graphics->GetFullscreenVertexBuffer().get());
builder.BindVertexBuffer(0, *graphics->GetFullscreenVertexBuffer());
builder.BindShaderBinding(0, *blitShaderBinding);
builder.Draw(3);
@@ -265,19 +325,35 @@ namespace Nz
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
for (const auto& [worldInstance, renderables] : m_renderables)
auto it = m_depthPrepassRenderQueue.begin();
while (it != m_depthPrepassRenderQueue.end())
{
builder.BindShaderBinding(Graphics::WorldBindingSet, worldInstance->GetShaderBinding());
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
for (const auto& [renderable, renderableData] : renderables)
renderable->Draw("DepthPass", builder);
m_temporaryElementList.push_back(element);
++it;
while (it != m_depthPrepassRenderQueue.end() && (*it)->GetElementType() == elementType)
{
m_temporaryElementList.push_back(*it);
++it;
}
if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType])
continue;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, m_temporaryElementList.data(), m_temporaryElementList.size());
m_temporaryElementList.clear();
}
});
FramePass& forwardPass = frameGraph.AddPass("Forward pass");
forwardPass.AddOutput(viewerData.colorAttachment);
forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment);
forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
//forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
forwardPass.SetClearColor(0, Color::Black);
forwardPass.SetDepthStencilClear(1.f, 0);
@@ -299,12 +375,28 @@ namespace Nz
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
for (const auto& [worldInstance, renderables] : m_renderables)
auto it = m_forwardRenderQueue.begin();
while (it != m_forwardRenderQueue.end())
{
builder.BindShaderBinding(Graphics::WorldBindingSet, worldInstance->GetShaderBinding());
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
for (const auto& [renderable, renderableData] : renderables)
renderable->Draw("ForwardPass", builder);
m_temporaryElementList.push_back(element);
++it;
while (it != m_forwardRenderQueue.end() && (*it)->GetElementType() == elementType)
{
m_temporaryElementList.push_back(*it);
++it;
}
if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType])
continue;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, m_temporaryElementList.data(), m_temporaryElementList.size());
m_temporaryElementList.clear();
}
});
}

View File

@@ -789,6 +789,8 @@ namespace Nz
if (first)
{
bool canDiscard = true;
// Check if a future pass reads from the DS buffer or if we can discard it after this pass
if (auto readIt = m_pending.attachmentReadList.find(dsInputAttachment); readIt != m_pending.attachmentReadList.end())
{
@@ -798,11 +800,13 @@ namespace Nz
if (readPhysicalPassIndex > physicalPassIndex) //< Read in a future pass?
{
// Yes, store it
dsAttachment.storeOp = AttachmentStoreOp::Store;
canDiscard = false;
break;
}
}
}
dsAttachment.storeOp = (canDiscard) ? AttachmentStoreOp::Discard : AttachmentStoreOp::Store;
}
depthStencilAttachment = RenderPass::AttachmentReference{

View File

@@ -6,6 +6,7 @@
#include <Nazara/Graphics/GraphicalMesh.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/RenderSubmesh.hpp>
#include <Nazara/Graphics/WorldInstance.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Graphics/Debug.hpp>
@@ -15,10 +16,10 @@ namespace Nz
Model::Model(std::shared_ptr<GraphicalMesh> graphicalMesh) :
m_graphicalMesh(std::move(graphicalMesh))
{
m_subMeshes.reserve(m_graphicalMesh->GetSubMeshCount());
m_submeshes.reserve(m_graphicalMesh->GetSubMeshCount());
for (std::size_t i = 0; i < m_graphicalMesh->GetSubMeshCount(); ++i)
{
auto& subMeshData = m_subMeshes.emplace_back();
auto& subMeshData = m_submeshes.emplace_back();
//subMeshData.material = DefaultMaterial; //< TODO
subMeshData.vertexBufferData = {
{
@@ -29,11 +30,11 @@ namespace Nz
}
}
void Model::Draw(const std::string& pass, CommandBufferBuilder& commandBuffer) const
void Model::BuildElement(const std::string& pass, 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)
{
const auto& submeshData = m_subMeshes[i];
const auto& submeshData = m_submeshes[i];
MaterialPass* materialPass = submeshData.material->GetPass(pass);
if (!materialPass)
@@ -43,12 +44,7 @@ namespace Nz
const auto& vertexBuffer = m_graphicalMesh->GetVertexBuffer(i);
const auto& renderPipeline = materialPass->GetPipeline()->GetRenderPipeline(submeshData.vertexBufferData);
commandBuffer.BindShaderBinding(Graphics::MaterialBindingSet, materialPass->GetShaderBinding());
commandBuffer.BindIndexBuffer(indexBuffer.get());
commandBuffer.BindVertexBuffer(0, vertexBuffer.get());
commandBuffer.BindPipeline(*renderPipeline);
commandBuffer.DrawIndexed(static_cast<Nz::UInt32>(m_graphicalMesh->GetIndexCount(i)));
elements.emplace_back(std::make_unique<RenderSubmesh>(0, renderPipeline, m_graphicalMesh->GetIndexCount(i), indexBuffer, vertexBuffer, worldInstance.GetShaderBinding(), materialPass->GetShaderBinding()));
}
}
@@ -64,20 +60,20 @@ namespace Nz
const std::shared_ptr<Material>& Model::GetMaterial(std::size_t subMeshIndex) const
{
assert(subMeshIndex < m_subMeshes.size());
const auto& subMeshData = m_subMeshes[subMeshIndex];
assert(subMeshIndex < m_submeshes.size());
const auto& subMeshData = m_submeshes[subMeshIndex];
return subMeshData.material;
}
std::size_t Model::GetMaterialCount() const
{
return m_subMeshes.size();
return m_submeshes.size();
}
const std::vector<RenderPipelineInfo::VertexBufferData>& Model::GetVertexBufferData(std::size_t subMeshIndex) const
{
assert(subMeshIndex < m_subMeshes.size());
const auto& subMeshData = m_subMeshes[subMeshIndex];
assert(subMeshIndex < m_submeshes.size());
const auto& subMeshData = m_submeshes[subMeshIndex];
return subMeshData.vertexBufferData;
}

View File

@@ -0,0 +1,11 @@
// 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/RenderElement.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
RenderElement::~RenderElement() = default;
}

View File

@@ -0,0 +1,32 @@
// 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/RenderQueue.hpp>
#include <algorithm>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
void RenderQueueInternal::Sort()
{
std::sort(m_orderedRenderQueue.begin(), m_orderedRenderQueue.end(), [](const RenderDataPair& lhs, const RenderDataPair& rhs)
{
/*
Original code:
if (lhs.first == rhs.first)
return lhs.second < rhs.second;
else
return lhs.first < rhs.first;
Clang seems to the the only one to prevent branching with the original code (using cmov)
Rewriting the code with bit ops seems to prevent branching with Clang/GCC/MSVC
*/
bool equal = lhs.first == rhs.first;
bool compareFirst = lhs.first < rhs.first;
bool compareSecond = lhs.second < rhs.second;
return (equal & compareSecond) | (!equal & compareFirst);
});
}
}

View File

@@ -0,0 +1,10 @@
// 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/RenderSubmesh.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
}

View File

@@ -0,0 +1,57 @@
// 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/SubmeshRenderer.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/RenderSubmesh.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
void SubmeshRenderer::Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount)
{
const AbstractBuffer* currentIndexBuffer = nullptr;
const AbstractBuffer* currentVertexBuffer = nullptr;
const RenderPipeline* currentPipeline = nullptr;
const ShaderBinding* currentMaterialBinding = nullptr;
for (std::size_t i = 0; i < elementCount; ++i)
{
assert(elements[i]->GetElementType() == UnderlyingCast(BasicRenderElement::Submesh));
const RenderSubmesh& submesh = static_cast<const RenderSubmesh&>(*elements[i]);
if (const RenderPipeline* pipeline = submesh.GetRenderPipeline(); currentPipeline != pipeline)
{
commandBuffer.BindPipeline(*pipeline);
currentPipeline = pipeline;
}
if (const ShaderBinding* materialBinding = &submesh.GetMaterialBinding(); currentMaterialBinding != materialBinding)
{
commandBuffer.BindShaderBinding(Graphics::MaterialBindingSet, *materialBinding);
currentMaterialBinding = materialBinding;
}
if (const AbstractBuffer* indexBuffer = submesh.GetIndexBuffer(); currentIndexBuffer != indexBuffer)
{
commandBuffer.BindIndexBuffer(*indexBuffer);
currentIndexBuffer = indexBuffer;
}
if (const AbstractBuffer* vertexBuffer = submesh.GetVertexBuffer(); currentVertexBuffer != vertexBuffer)
{
commandBuffer.BindVertexBuffer(0, *vertexBuffer);
currentVertexBuffer = vertexBuffer;
}
commandBuffer.BindShaderBinding(Graphics::WorldBindingSet, submesh.GetInstanceBinding());
if (currentIndexBuffer)
commandBuffer.DrawIndexed(submesh.GetIndexCount());
else
commandBuffer.Draw(submesh.GetIndexCount());
}
}
}