Graphics: Add RenderSystem and frame pipeline

This commit is contained in:
Jérôme Leclercq
2021-07-06 11:04:22 +02:00
parent 428a706fbe
commit 4ac5fe7cba
37 changed files with 1202 additions and 141 deletions

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/AbstractViewer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
}

View File

@@ -0,0 +1,28 @@
// Copyright (C) 2021 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <Nazara/Graphics/Components/CameraComponent.hpp>
#include <stdexcept>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
const RenderTarget& CameraComponent::GetRenderTarget()
{
if (!m_renderTarget)
throw std::runtime_error("no rendertarget set");
return* m_renderTarget;
}
ViewerInstance& CameraComponent::GetViewerInstance()
{
return m_viewerInstance;
}
const ViewerInstance& CameraComponent::GetViewerInstance() const
{
return m_viewerInstance;
}
}

View File

@@ -0,0 +1,296 @@
// 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/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>
namespace Nz
{
ForwardFramePipeline::ForwardFramePipeline() :
m_rebuildFrameGraph(true),
m_rebuildForwardPass(false)
{
}
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
{
m_invalidatedViewerInstances.insert(viewerInstance);
}
void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance)
{
m_invalidatedWorldInstances.insert(worldInstance);
}
void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstance* worldInstance, const InstancedRenderable* instancedRenderable)
{
auto& renderableMap = m_renderables[worldInstance];
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)
RegisterMaterial(newMaterial.get());
const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex);
if (prevMaterial)
UnregisterMaterial(prevMaterial.get());
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())
RegisterMaterial(mat);
}
m_rebuildForwardPass = true;
}
}
void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance)
{
m_viewers.emplace(viewerInstance, ViewerData{});
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
{
Graphics* graphics = Graphics::Instance();
if (m_rebuildFrameGraph)
{
renderFrame.PushForRelease(std::move(m_bakedFrameGraph));
m_bakedFrameGraph = BuildFrameGraph();
m_rebuildForwardPass = false; //< No need to rebuild forward pass twice
}
// 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 (Material* material : m_invalidatedMaterials)
{
if (material->Update(renderFrame, builder))
m_rebuildForwardPass = true;
}
m_invalidatedMaterials.clear();
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, QueueType::Transfer);
const Vector2ui& frameSize = renderFrame.GetSize();
if (m_bakedFrameGraph.Resize(frameSize.x, frameSize.y))
{
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);
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);
{
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().get());
builder.BindShaderBinding(0, *blitShaderBinding);
builder.Draw(3);
}
builder.EndRenderPass();
}
builder.EndDebugRegion();
}, QueueType::Graphics);
}
}
void ForwardFramePipeline::UnregisterInstancedDrawable(WorldInstance* 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_renderables.erase(worldInstance);
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (Material* mat = instancedRenderable->GetMaterial(i).get())
UnregisterMaterial(mat);
}
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& framePass = frameGraph.AddPass("Forward pass");
framePass.AddOutput(viewerData.colorAttachment);
framePass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
framePass.SetClearColor(0, Color::Black);
framePass.SetDepthStencilClear(1.f, 0);
framePass.SetExecutionCallback([this]()
{
if (m_rebuildForwardPass)
{
m_rebuildForwardPass = false;
return FramePassExecution::UpdateAndExecute;
}
else
return FramePassExecution::Execute;
});
framePass.SetCommandCallback([this, viewer = viewer](CommandBufferBuilder& builder, const Recti& renderRect)
{
builder.SetScissor(renderRect);
builder.SetViewport(renderRect);
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
for (const auto& [worldInstance, renderables] : m_renderables)
{
builder.BindShaderBinding(Graphics::WorldBindingSet, worldInstance->GetShaderBinding());
for (const auto& [renderable, renderableData] : renderables)
renderable->Draw(builder);
}
});
}
//FIXME: This doesn't handle multiple window viewers
for (auto&& [viewer, viewerData] : m_viewers)
frameGraph.SetBackbufferOutput(viewerData.colorAttachment);
return frameGraph.Bake();
}
void ForwardFramePipeline::RegisterMaterial(Material* 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 Material* /*material*/)
{
m_invalidatedMaterials.insert(material);
});
m_invalidatedMaterials.insert(material);
}
it->second.usedCount++;
}
void ForwardFramePipeline::UnregisterMaterial(Material* 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

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

View File

@@ -11,13 +11,21 @@
namespace Nz
{
namespace
{
const UInt8 r_blitShader[] = {
#include <Nazara/Graphics/Resources/Shaders/blit.nzsl.h>
};
}
/*!
* \ingroup graphics
* \class Nz::Graphics
* \brief Graphics class that represents the module initializer of Graphics
*/
Graphics::Graphics(Config config) :
ModuleBase("Graphics", this)
ModuleBase("Graphics", this),
m_preferredDepthStencilFormat(PixelFormat::Undefined)
{
ECS::RegisterComponents();
@@ -60,11 +68,20 @@ namespace Nz
FillWorldPipelineLayout(referenceLayoutInfo);
m_referencePipelineLayout = m_renderDevice->InstantiateRenderPipelineLayout(std::move(referenceLayoutInfo));
BuildFullscreenVertexBuffer();
BuildBlitPipeline();
SelectDepthStencilFormats();
}
Graphics::~Graphics()
{
MaterialPipeline::Uninitialize();
m_samplerCache.reset();
m_fullscreenVertexBuffer.reset();
m_fullscreenVertexDeclaration.reset();
m_blitPipeline.reset();
m_blitPipelineLayout.reset();
}
void Graphics::FillViewerPipelineLayout(RenderPipelineLayoutInfo& layoutInfo, UInt32 set)
@@ -85,5 +102,80 @@ namespace Nz
});
}
void Graphics::BuildBlitPipeline()
{
RenderPipelineLayoutInfo layoutInfo;
layoutInfo.bindings.assign({
{
0, 0,
ShaderBindingType::Texture,
ShaderStageType::Fragment
}
});
m_blitPipelineLayout = m_renderDevice->InstantiateRenderPipelineLayout(std::move(layoutInfo));
if (!m_blitPipelineLayout)
throw std::runtime_error("failed to instantiate fullscreen renderpipeline layout");
auto blitShader = m_renderDevice->InstantiateShaderModule(ShaderStageType::Fragment | ShaderStageType::Vertex, ShaderLanguage::NazaraShader, r_blitShader, sizeof(r_blitShader), {});
if (!blitShader)
throw std::runtime_error("failed to instantiate blit shader");
RenderPipelineInfo pipelineInfo;
pipelineInfo.pipelineLayout = m_blitPipelineLayout;
pipelineInfo.shaderModules.push_back(std::move(blitShader));
pipelineInfo.vertexBuffers.assign({
{
0,
m_fullscreenVertexDeclaration
}
});
m_blitPipeline = m_renderDevice->InstantiateRenderPipeline(std::move(pipelineInfo));
}
void Graphics::BuildFullscreenVertexBuffer()
{
m_fullscreenVertexDeclaration = VertexDeclaration::Get(VertexLayout::XY_UV);
std::array<Nz::VertexStruct_XY_UV, 3> vertexData = {
{
{
Nz::Vector2f(-1.f, 1.f),
Nz::Vector2f(0.0f, 1.0f),
},
{
Nz::Vector2f(-1.f, -3.f),
Nz::Vector2f(0.0f, -1.0f),
},
{
Nz::Vector2f(3.f, 1.f),
Nz::Vector2f(2.0f, 1.0f),
}
}
};
m_fullscreenVertexBuffer = m_renderDevice->InstantiateBuffer(BufferType::Vertex);
if (!m_fullscreenVertexBuffer->Initialize(m_fullscreenVertexDeclaration->GetStride() * vertexData.size(), BufferUsage::DeviceLocal))
throw std::runtime_error("failed to initialize fullscreen vertex buffer");
if (!m_fullscreenVertexBuffer->Fill(vertexData.data(), 0, m_fullscreenVertexDeclaration->GetStride() * vertexData.size()))
throw std::runtime_error("failed to fill fullscreen vertex buffer");
}
void Graphics::SelectDepthStencilFormats()
{
for (PixelFormat depthStencilCandidate : { PixelFormat::Depth24Stencil8, PixelFormat::Depth32FStencil8, PixelFormat::Depth16Stencil8 })
{
if (m_renderDevice->IsTextureFormatSupported(depthStencilCandidate, TextureUsage::DepthStencilAttachment))
{
m_preferredDepthStencilFormat = depthStencilCandidate;
break;
}
}
if (m_preferredDepthStencilFormat == PixelFormat::Undefined)
throw std::runtime_error("no supported depth-stencil format found");
}
Graphics* Graphics::s_instance = nullptr;
}

View File

@@ -29,10 +29,8 @@ namespace Nz
}
}
void Model::Draw(CommandBufferBuilder& commandBuffer, const WorldInstance& instance) const
void Model::Draw(CommandBufferBuilder& commandBuffer) const
{
commandBuffer.BindShaderBinding(Graphics::WorldBindingSet, instance.GetShaderBinding());
for (std::size_t i = 0; i < m_subMeshes.size(); ++i)
{
const auto& submeshData = m_subMeshes[i];
@@ -66,6 +64,11 @@ namespace Nz
return subMeshData.material;
}
std::size_t Model::GetMaterialCount() const
{
return m_subMeshes.size();
}
const std::shared_ptr<RenderPipeline>& Model::GetRenderPipeline(std::size_t subMeshIndex) const
{
assert(subMeshIndex < m_subMeshes.size());

View File

@@ -0,0 +1,40 @@
external
{
[binding(0)] texture: sampler2D<f32>
}
struct VertIn
{
[location(0)] position: vec2<f32>,
[location(1)] uv: vec2<f32>
}
struct VertOut
{
[builtin(position)] position: vec4<f32>,
[location(0)] uv: vec2<f32>
}
[entry(vert)]
fn main(vertIn: VertIn) -> VertOut
{
let output: VertOut;
output.position = vec4<f32>(vertIn.position, 0.0, 1.0);
output.uv = vertIn.uv;
return output;
}
struct FragOut
{
[location(0)] color: vec4<f32>
}
[entry(frag)]
fn main(input: VertOut) -> FragOut
{
let output: FragOut;
output.color = texture.Sample(input.uv);
return output;
}

View File

@@ -0,0 +1 @@
101,120,116,101,114,110,97,108,10,123,10,32,32,32,32,91,98,105,110,100,105,110,103,40,48,41,93,32,116,101,120,116,117,114,101,58,32,115,97,109,112,108,101,114,50,68,60,102,51,50,62,10,125,10,10,115,116,114,117,99,116,32,86,101,114,116,73,110,10,123,10,32,32,32,32,91,108,111,99,97,116,105,111,110,40,48,41,93,32,112,111,115,105,116,105,111,110,58,32,118,101,99,50,60,102,51,50,62,44,10,32,32,32,32,91,108,111,99,97,116,105,111,110,40,49,41,93,32,117,118,58,32,118,101,99,50,60,102,51,50,62,10,125,10,10,115,116,114,117,99,116,32,86,101,114,116,79,117,116,10,123,10,32,32,32,32,91,98,117,105,108,116,105,110,40,112,111,115,105,116,105,111,110,41,93,32,112,111,115,105,116,105,111,110,58,32,118,101,99,52,60,102,51,50,62,44,10,32,32,32,32,91,108,111,99,97,116,105,111,110,40,48,41,93,32,117,118,58,32,118,101,99,50,60,102,51,50,62,10,125,10,10,91,101,110,116,114,121,40,118,101,114,116,41,93,10,102,110,32,109,97,105,110,40,118,101,114,116,73,110,58,32,86,101,114,116,73,110,41,32,45,62,32,86,101,114,116,79,117,116,10,123,10,32,32,32,32,108,101,116,32,111,117,116,112,117,116,58,32,86,101,114,116,79,117,116,59,10,32,32,32,32,111,117,116,112,117,116,46,112,111,115,105,116,105,111,110,32,61,32,118,101,99,52,60,102,51,50,62,40,118,101,114,116,73,110,46,112,111,115,105,116,105,111,110,44,32,48,46,48,44,32,49,46,48,41,59,10,32,32,32,32,111,117,116,112,117,116,46,117,118,32,61,32,118,101,114,116,73,110,46,117,118,59,10,10,32,32,32,32,114,101,116,117,114,110,32,111,117,116,112,117,116,59,10,125,10,10,115,116,114,117,99,116,32,70,114,97,103,79,117,116,10,123,10,32,32,32,32,91,108,111,99,97,116,105,111,110,40,48,41,93,32,99,111,108,111,114,58,32,118,101,99,52,60,102,51,50,62,10,125,10,10,91,101,110,116,114,121,40,102,114,97,103,41,93,10,102,110,32,109,97,105,110,40,105,110,112,117,116,58,32,86,101,114,116,79,117,116,41,32,45,62,32,70,114,97,103,79,117,116,10,123,10,32,32,32,32,108,101,116,32,111,117,116,112,117,116,58,32,70,114,97,103,79,117,116,59,10,32,32,32,32,111,117,116,112,117,116,46,99,111,108,111,114,32,61,32,116,101,120,116,117,114,101,46,83,97,109,112,108,101,40,105,110,112,117,116,46,117,118,41,59,10,10,32,32,32,32,114,101,116,117,114,110,32,111,117,116,112,117,116,59,10,125,10,

View File

@@ -0,0 +1,149 @@
// Copyright (C) 2021 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
#include <Nazara/Graphics/Systems/RenderSystem.hpp>
#include <Nazara/Graphics/ForwardFramePipeline.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp>
#include <Nazara/Graphics/WorldInstance.hpp>
#include <Nazara/Graphics/Components/CameraComponent.hpp>
#include <Nazara/Graphics/Components/GraphicsComponent.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/Renderframe.hpp>
#include <Nazara/Renderer/UploadPool.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
RenderSystem::RenderSystem(entt::registry& registry) :
m_cameraConstructObserver(registry, entt::collector.group<CameraComponent, NodeComponent>()),
m_graphicsConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent>())
{
m_cameraDestroyConnection = registry.on_destroy<CameraComponent>().connect<&RenderSystem::OnCameraDestroy>(this);
m_graphicsDestroyConnection = registry.on_destroy<GraphicsComponent>().connect<&RenderSystem::OnGraphicsDestroy>(this);
m_nodeDestroyConnection = registry.on_destroy<NodeComponent>().connect<&RenderSystem::OnNodeDestroy>(this);
m_pipeline = std::make_unique<ForwardFramePipeline>();
}
RenderSystem::~RenderSystem()
{
m_cameraConstructObserver.disconnect();
m_graphicsConstructObserver.disconnect();
m_cameraDestroyConnection.release();
m_graphicsDestroyConnection.release();
m_nodeDestroyConnection.release();
}
void RenderSystem::Render(entt::registry& registry, RenderFrame& renderFrame)
{
m_cameraConstructObserver.each([&](entt::entity entity)
{
CameraComponent& entityCamera = registry.get<CameraComponent>(entity);
NodeComponent& entityNode = registry.get<NodeComponent>(entity);
m_pipeline->RegisterViewer(&entityCamera);
m_invalidatedCameraNode.insert(entity);
assert(m_cameraEntities.find(entity) == m_cameraEntities.end());
auto& cameraEntity = m_cameraEntities[entity];
cameraEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* node)
{
m_invalidatedCameraNode.insert(entity);
});
});
m_graphicsConstructObserver.each([&](entt::entity entity)
{
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
NodeComponent& entityNode = registry.get<NodeComponent>(entity);
WorldInstance& worldInstance = entityGfx.GetWorldInstance();
for (const auto& renderable : entityGfx.GetRenderables())
m_pipeline->RegisterInstancedDrawable(&worldInstance, renderable.get());
m_invalidatedWorldNode.insert(entity);
assert(m_graphicsEntities.find(entity) == m_graphicsEntities.end());
auto& graphicsEntity = m_graphicsEntities[entity];
graphicsEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* node)
{
m_invalidatedWorldNode.insert(entity);
});
graphicsEntity.onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this](GraphicsComponent* gfx, const std::shared_ptr<InstancedRenderable>& renderable)
{
WorldInstance& worldInstance = gfx->GetWorldInstance();
m_pipeline->RegisterInstancedDrawable(&worldInstance, renderable.get());
});
graphicsEntity.onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this](GraphicsComponent* gfx, const std::shared_ptr<InstancedRenderable>& renderable)
{
WorldInstance& worldInstance = gfx->GetWorldInstance();
m_pipeline->UnregisterInstancedDrawable(&worldInstance, renderable.get());
});
});
UpdateInstances(registry);
m_pipeline->Render(renderFrame);
}
void RenderSystem::OnCameraDestroy(entt::registry& registry, entt::entity entity)
{
m_cameraEntities.erase(entity);
m_invalidatedCameraNode.erase(entity);
CameraComponent& entityCamera = registry.get<CameraComponent>(entity);
m_pipeline->UnregisterViewer(&entityCamera);
}
void RenderSystem::OnGraphicsDestroy(entt::registry& registry, entt::entity entity)
{
m_graphicsEntities.erase(entity);
m_invalidatedWorldNode.erase(entity);
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
WorldInstance& worldInstance = entityGfx.GetWorldInstance();
for (const auto& renderable : entityGfx.GetRenderables())
m_pipeline->UnregisterInstancedDrawable(&worldInstance, renderable.get());
}
void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity)
{
if (registry.try_get<CameraComponent>(entity))
OnCameraDestroy(registry, entity);
if (registry.try_get<GraphicsComponent>(entity))
OnGraphicsDestroy(registry, entity);
}
void RenderSystem::UpdateInstances(entt::registry& registry)
{
for (entt::entity entity : m_invalidatedCameraNode)
{
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
CameraComponent& entityCamera = registry.get<CameraComponent>(entity);
ViewerInstance& viewerInstance = entityCamera.GetViewerInstance();
viewerInstance.UpdateViewMatrix(Nz::Matrix4f::ViewMatrix(entityNode.GetPosition(CoordSys::Global), entityNode.GetRotation(CoordSys::Global)));
m_pipeline->InvalidateViewer(&entityCamera);
}
m_invalidatedCameraNode.clear();
for (entt::entity entity : m_invalidatedWorldNode)
{
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
GraphicsComponent& entityGraphics = registry.get<GraphicsComponent>(entity);
WorldInstance& worldInstance = entityGraphics.GetWorldInstance();
worldInstance.UpdateWorldMatrix(entityNode.GetTransformMatrix());
m_pipeline->InvalidateWorldInstance(&worldInstance);
}
m_invalidatedWorldNode.clear();
}
}