Graphics: Add MaterialPassRegistry

This commit is contained in:
Jérôme Leclercq 2021-07-29 20:16:06 +02:00
parent 5b1123b971
commit 9a311da2c8
20 changed files with 181 additions and 52 deletions

View File

@ -9,6 +9,7 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <memory>
#include <vector>
@ -18,7 +19,7 @@ namespace Nz
class CommandBufferBuilder;
class RenderElement;
class ElementRenderer
class NAZARA_GRAPHICS_API ElementRenderer
{
public:
ElementRenderer() = default;

View File

@ -73,6 +73,8 @@ namespace Nz
ShaderBindingPtr blitShaderBinding;
};
std::size_t m_depthPassIndex;
std::size_t m_forwardPassIndex;
std::unordered_map<AbstractViewer*, ViewerData> m_viewers;
std::unordered_map<MaterialPass*, MaterialData> m_materials;
std::unordered_map<WorldInstancePtr, std::unordered_map<const InstancedRenderable*, RenderableData>> m_renderables;

View File

@ -9,6 +9,7 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/MaterialPassRegistry.hpp>
#include <Nazara/Graphics/TextureSamplerCache.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
@ -36,6 +37,8 @@ namespace Nz
inline const std::shared_ptr<RenderPipelineLayout>& GetBlitPipelineLayout() const;
inline const std::shared_ptr<AbstractBuffer>& GetFullscreenVertexBuffer() const;
inline const std::shared_ptr<VertexDeclaration>& GetFullscreenVertexDeclaration() const;
inline MaterialPassRegistry& GetMaterialPassRegistry();
inline const MaterialPassRegistry& GetMaterialPassRegistry() const;
inline PixelFormat GetPreferredDepthStencilFormat() const;
inline const std::shared_ptr<RenderPipelineLayout>& GetReferencePipelineLayout() const;
inline const std::shared_ptr<RenderDevice>& GetRenderDevice() const;
@ -58,6 +61,7 @@ namespace Nz
private:
void BuildBlitPipeline();
void BuildFullscreenVertexBuffer();
void RegisterMaterialPasses();
void SelectDepthStencilFormats();
std::optional<RenderPassCache> m_renderPassCache;
@ -68,6 +72,7 @@ namespace Nz
std::shared_ptr<RenderPipelineLayout> m_blitPipelineLayout;
std::shared_ptr<RenderPipelineLayout> m_referencePipelineLayout;
std::shared_ptr<VertexDeclaration> m_fullscreenVertexDeclaration;
MaterialPassRegistry m_materialPassRegistry;
PixelFormat m_preferredDepthStencilFormat;
static Graphics* s_instance;

View File

@ -27,6 +27,16 @@ namespace Nz
return m_fullscreenVertexDeclaration;
}
inline MaterialPassRegistry& Graphics::GetMaterialPassRegistry()
{
return m_materialPassRegistry;
}
inline const MaterialPassRegistry& Graphics::GetMaterialPassRegistry() const
{
return m_materialPassRegistry;
}
inline PixelFormat Graphics::GetPreferredDepthStencilFormat() const
{
return m_preferredDepthStencilFormat;

View File

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

View File

@ -8,6 +8,7 @@
#define NAZARA_MATERIAL_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/MaterialPass.hpp>
namespace Nz
@ -18,16 +19,18 @@ namespace Nz
Material();
~Material() = default;
void AddPass(std::string name, std::shared_ptr<MaterialPass> pass);
inline void AddPass(std::size_t passIndex, std::shared_ptr<MaterialPass> pass);
inline void AddPass(std::string passName, std::shared_ptr<MaterialPass> pass);
inline MaterialPass* GetPass(const std::string& name) const;
inline MaterialPass* GetPass(std::size_t passIndex) const;
bool HasPass(const std::string& name) const;
inline bool HasPass(std::size_t passIndex) const;
void RemovePass(const std::string& name);
inline void RemovePass(std::size_t passIndex);
inline void RemovePass(const std::string& passName);
private:
std::unordered_map<std::string, std::shared_ptr<MaterialPass>> m_passes;
std::vector<std::shared_ptr<MaterialPass>> m_passes;
};
}

View File

@ -7,13 +7,45 @@
namespace Nz
{
inline MaterialPass* Material::GetPass(const std::string& name) const
inline void Material::AddPass(std::size_t passIndex, std::shared_ptr<MaterialPass> pass)
{
auto it = m_passes.find(name);
if (it == m_passes.end())
if (passIndex >= m_passes.size())
m_passes.resize(passIndex + 1);
m_passes[passIndex] = std::move(pass);
}
inline void Material::AddPass(std::string passName, std::shared_ptr<MaterialPass> pass)
{
auto& registry = Graphics::Instance()->GetMaterialPassRegistry();
return AddPass(registry.GetPassIndex(passName), std::move(pass));
}
inline MaterialPass* Material::GetPass(std::size_t passIndex) const
{
if (passIndex >= m_passes.size())
return nullptr;
return it->second.get();
return m_passes[passIndex].get();
}
inline bool Material::HasPass(std::size_t passIndex) const
{
return GetPass(passIndex) != nullptr;
}
inline void Material::RemovePass(std::size_t passIndex)
{
if (passIndex >= m_passes.size())
return;
m_passes[passIndex].reset();
}
inline void Material::RemovePass(const std::string& passName)
{
auto& registry = Graphics::Instance()->GetMaterialPassRegistry();
return RemovePass(registry.GetPassIndex(passName));
}
}

View File

@ -133,6 +133,7 @@ namespace Nz
UInt64 m_enabledOptions;
mutable MaterialPipelineInfo m_pipelineInfo;
ShaderBindingPtr m_shaderBinding;
bool m_forceCommandBufferRegeneration;
mutable bool m_pipelineUpdated;
bool m_shaderBindingUpdated;
};

View File

@ -621,13 +621,17 @@ namespace Nz
inline void MaterialPass::InvalidatePipeline()
{
m_forceCommandBufferRegeneration = true;
m_pipelineUpdated = false;
OnMaterialInvalidated(this);
}
inline void MaterialPass::InvalidateShaderBinding()
{
m_forceCommandBufferRegeneration = true;
m_shaderBindingUpdated = false;
OnMaterialInvalidated(this);
}

View File

@ -0,0 +1,38 @@
// 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
#pragma once
#ifndef NAZARA_MATERIAL_PASS_REGISTRY_HPP
#define NAZARA_MATERIAL_PASS_REGISTRY_HPP
#include <Nazara/Prerequisites.hpp>
#include <string>
#include <unordered_map>
namespace Nz
{
class MaterialPassRegistry
{
public:
MaterialPassRegistry() = default;
MaterialPassRegistry(const MaterialPassRegistry&) = default;
MaterialPassRegistry(MaterialPassRegistry&&) = default;
~MaterialPassRegistry() = default;
inline std::size_t GetPassIndex(const std::string& passName) const;
inline std::size_t RegisterPass(std::string passName);
MaterialPassRegistry& operator=(const MaterialPassRegistry&) = default;
MaterialPassRegistry& operator=(MaterialPassRegistry&&) = default;
private:
std::unordered_map<std::string, std::size_t> m_passIndex;
};
}
#include <Nazara/Graphics/MaterialPassRegistry.inl>
#endif

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/MaterialPassRegistry.hpp>
#include <stdexcept>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
std::size_t MaterialPassRegistry::GetPassIndex(const std::string& passName) const
{
auto it = m_passIndex.find(passName);
if (it == m_passIndex.end())
throw std::runtime_error("pass " + passName + " must be registered before being used");
return it->second;
}
inline std::size_t MaterialPassRegistry::RegisterPass(std::string passName)
{
if (m_passIndex.find(passName) != m_passIndex.end())
throw std::runtime_error("pass " + passName + " is already registered");
std::size_t passIndex = m_passIndex.size();
m_passIndex.emplace(std::move(passName), passIndex);
return passIndex;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

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

View File

@ -21,8 +21,8 @@ namespace Nz
inline UInt64 RenderSubmesh::ComputeSortingScore(const RenderQueueRegistry& registry) const
{
UInt64 elementType = GetElementType();
UInt64 layerIndex = registry.FetchLayerIndex(m_renderLayer);
UInt64 elementType = GetElementType();
UInt64 pipelineIndex = registry.FetchPipelineIndex(m_renderPipeline.get());
UInt64 vertexBufferIndex = registry.FetchVertexBuffer(m_vertexBuffer.get());
@ -33,8 +33,8 @@ namespace Nz
// - VertexBuffer (8bits)
// - ?? (24bits) - Depth?
return (elementType & 0xF) << 60 |
(layerIndex & 0xFF) << 52 |
return (layerIndex & 0xFF) << 60 |
(elementType & 0xF) << 52 |
(pipelineIndex & 0xFFFF) << 36 |
(vertexBufferIndex & 0xFF) << 24;
}

View File

@ -12,7 +12,7 @@
namespace Nz
{
class SubmeshRenderer : public ElementRenderer
class NAZARA_GRAPHICS_API SubmeshRenderer : public ElementRenderer
{
public:
SubmeshRenderer() = default;

View File

@ -27,6 +27,10 @@ namespace Nz
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>();
}
@ -46,6 +50,9 @@ namespace Nz
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;
@ -53,20 +60,20 @@ namespace Nz
{
if (newMaterial)
{
if (MaterialPass* pass = newMaterial->GetPass("DepthPass"))
if (MaterialPass* pass = newMaterial->GetPass(m_depthPassIndex))
RegisterMaterialPass(pass);
if (MaterialPass* pass = newMaterial->GetPass("ForwardPass"))
if (MaterialPass* pass = newMaterial->GetPass(m_forwardPassIndex))
RegisterMaterialPass(pass);
}
const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex);
if (prevMaterial)
{
if (MaterialPass* pass = prevMaterial->GetPass("DepthPass"))
if (MaterialPass* pass = prevMaterial->GetPass(m_depthPassIndex))
UnregisterMaterialPass(pass);
if (MaterialPass* pass = prevMaterial->GetPass("ForwardPass"))
if (MaterialPass* pass = prevMaterial->GetPass(m_forwardPassIndex))
UnregisterMaterialPass(pass);
}
@ -79,10 +86,10 @@ namespace Nz
{
if (Material* mat = instancedRenderable->GetMaterial(i).get())
{
if (MaterialPass* pass = mat->GetPass("DepthPass"))
if (MaterialPass* pass = mat->GetPass(m_depthPassIndex))
RegisterMaterialPass(pass);
if (MaterialPass* pass = mat->GetPass("ForwardPass"))
if (MaterialPass* pass = mat->GetPass(m_forwardPassIndex))
RegisterMaterialPass(pass);
}
}
@ -95,6 +102,7 @@ namespace Nz
void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance)
{
m_viewers.emplace(viewerInstance, ViewerData{});
m_invalidatedViewerInstances.insert(viewerInstance);
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
@ -151,7 +159,7 @@ namespace Nz
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
renderable->BuildElement("DepthPass", *worldInstance, m_depthPrepassRenderElements);
renderable->BuildElement(m_depthPassIndex, *worldInstance, m_depthPrepassRenderElements);
}
}
@ -162,7 +170,7 @@ namespace Nz
for (const auto& [worldInstance, renderables] : m_renderables)
{
for (const auto& [renderable, renderableData] : renderables)
renderable->BuildElement("ForwardPass", *worldInstance, m_forwardRenderElements);
renderable->BuildElement(m_forwardPassIndex, *worldInstance, m_forwardRenderElements);
}
}
@ -280,10 +288,10 @@ namespace Nz
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass("DepthPass"))
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_depthPassIndex))
UnregisterMaterialPass(pass);
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass("ForwardPass"))
if (MaterialPass* pass = instancedRenderable->GetMaterial(i)->GetPass(m_forwardPassIndex))
UnregisterMaterialPass(pass);
}
@ -395,6 +403,9 @@ namespace Nz
void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue)
{
if (renderQueue.empty())
return;
auto it = renderQueue.begin();
auto itEnd = renderQueue.end();
while (it != itEnd)

View File

@ -90,9 +90,10 @@ namespace Nz
for (const auto& output : framePass.GetOutputs())
bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, output.attachmentId));
if (std::size_t attachmentId = framePass.GetDepthStencilOutput(); attachmentId != FramePass::InvalidAttachmentId)
std::size_t attachmentId;
if (attachmentId = framePass.GetDepthStencilOutput(); attachmentId != FramePass::InvalidAttachmentId)
bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, attachmentId));
else if (std::size_t attachmentId = framePass.GetDepthStencilInput(); attachmentId != FramePass::InvalidAttachmentId)
else if (attachmentId = framePass.GetDepthStencilInput(); attachmentId != FramePass::InvalidAttachmentId)
bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, attachmentId)); //< FIXME?
}
}
@ -292,7 +293,6 @@ namespace Nz
std::size_t dsInputAttachment = framePass.GetDepthStencilInput();
std::size_t dsOutputAttachement = framePass.GetDepthStencilOutput();
bool depthRead = false;
if (dsInputAttachment != FramePass::InvalidAttachmentId && dsOutputAttachement != FramePass::InvalidAttachmentId)
{

View File

@ -73,6 +73,7 @@ namespace Nz
BuildFullscreenVertexBuffer();
BuildBlitPipeline();
RegisterMaterialPasses();
SelectDepthStencilFormats();
}
@ -165,6 +166,12 @@ namespace Nz
throw std::runtime_error("failed to fill fullscreen vertex buffer");
}
void Graphics::RegisterMaterialPasses()
{
m_materialPassRegistry.RegisterPass("DepthPass");
m_materialPassRegistry.RegisterPass("ForwardPass");
}
void Graphics::SelectDepthStencilFormats()
{
for (PixelFormat depthStencilCandidate : { PixelFormat::Depth24Stencil8, PixelFormat::Depth32FStencil8, PixelFormat::Depth16Stencil8 })

View File

@ -10,22 +10,4 @@ namespace Nz
Material::Material()
{
}
void Material::AddPass(std::string name, std::shared_ptr<MaterialPass> pass)
{
if (HasPass(name))
return;
m_passes.emplace(std::move(name), std::move(pass));
}
bool Material::HasPass(const std::string& name) const
{
return m_passes.find(name) != m_passes.end();
}
void Material::RemovePass(const std::string& name)
{
m_passes.erase(name);
}
}

View File

@ -29,6 +29,7 @@ namespace Nz
MaterialPass::MaterialPass(std::shared_ptr<const MaterialSettings> settings) :
m_settings(std::move(settings)),
m_enabledOptions(0),
m_forceCommandBufferRegeneration(false),
m_pipelineUpdated(false),
m_shaderBindingUpdated(false)
{
@ -66,11 +67,8 @@ namespace Nz
bool MaterialPass::Update(RenderFrame& renderFrame, CommandBufferBuilder& builder)
{
bool shouldRegenerateCommandBuffer = false;
if (!m_shaderBindingUpdated)
{
shouldRegenerateCommandBuffer = true;
renderFrame.PushForRelease(std::move(m_shaderBinding));
m_shaderBinding.reset();
@ -92,6 +90,9 @@ namespace Nz
}
}
bool shouldRegenerateCommandBuffer = m_forceCommandBufferRegeneration;
m_forceCommandBufferRegeneration = false;
return shouldRegenerateCommandBuffer;
}

View File

@ -30,13 +30,13 @@ namespace Nz
}
}
void Model::BuildElement(const std::string& pass, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const
void Model::BuildElement(std::size_t passIndex, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const
{
for (std::size_t i = 0; i < m_submeshes.size(); ++i)
{
const auto& submeshData = m_submeshes[i];
MaterialPass* materialPass = submeshData.material->GetPass(pass);
MaterialPass* materialPass = submeshData.material->GetPass(passIndex);
if (!materialPass)
continue;