Graphics/FramePipeline: Replace maps with memory pools and indices

This commit is contained in:
Jérôme Leclercq
2022-02-21 20:47:11 +01:00
parent a1b6f51398
commit 20a86312ff
14 changed files with 598 additions and 354 deletions

View File

@@ -27,117 +27,49 @@
namespace Nz
{
ForwardFramePipeline::ForwardFramePipeline() :
m_renderablePool(4096),
m_lightPool(64),
m_viewerPool(8),
m_worldInstances(2048),
m_rebuildFrameGraph(true)
{
auto& passRegistry = Graphics::Instance()->GetMaterialPassRegistry();
m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass");
}
ForwardFramePipeline::~ForwardFramePipeline()
{
// Force viewer passes to unregister their materials
m_viewers.clear();
m_viewerPool.Clear();
}
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
void ForwardFramePipeline::InvalidateViewer(std::size_t viewerIndex)
{
m_invalidatedViewerInstances.insert(viewerInstance);
m_invalidatedViewerInstances.Set(viewerIndex);
}
void ForwardFramePipeline::InvalidateWorldInstance(WorldInstance* worldInstance)
void ForwardFramePipeline::InvalidateWorldInstance(std::size_t worldInstanceIndex)
{
m_invalidatedWorldInstances.insert(worldInstance);
m_invalidatedWorldInstances.Set(worldInstanceIndex);
}
void ForwardFramePipeline::RegisterInstancedDrawable(WorldInstancePtr worldInstance, const InstancedRenderable* instancedRenderable, UInt32 renderMask)
std::size_t ForwardFramePipeline::RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask)
{
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.renderMask = renderMask;
renderableData.onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [=](InstancedRenderable* /*instancedRenderable*/)
{
// TODO: Invalidate only relevant viewers and passes
for (auto&& [viewer, viewerData] : m_viewers)
{
UInt32 viewerRenderMask = viewer->GetRenderMask();
if (viewerRenderMask & renderMask)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->ForceInvalidation();
viewerData.forwardPass->ForceInvalidation();
}
}
});
renderableData.onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr<Material>& newMaterial)
{
if (newMaterial)
{
for (auto&& [viewer, viewerData] : m_viewers)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterMaterial(*newMaterial);
viewerData.forwardPass->RegisterMaterial(*newMaterial);
}
}
const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex);
if (prevMaterial)
{
for (auto&& [viewer, viewerData] : m_viewers)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->UnregisterMaterial(*prevMaterial);
viewerData.forwardPass->UnregisterMaterial(*prevMaterial);
}
}
});
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (Material* mat = instancedRenderable->GetMaterial(i).get())
{
for (auto&& [viewer, viewerData] : m_viewers)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterMaterial(*mat);
viewerData.forwardPass->RegisterMaterial(*mat);
}
}
}
}
}
void ForwardFramePipeline::RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask)
{
auto& lightData = m_lights[light.get()];
lightData.light = std::move(light);
lightData.renderMask = renderMask;
lightData.onLightInvalidated.Connect(lightData.light->OnLightDataInvalided, [=](Light*)
std::size_t lightIndex;
LightData* lightData = m_lightPool.Allocate(lightIndex);
lightData->light = std::move(light);
lightData->renderMask = renderMask;
lightData->onLightInvalidated.Connect(lightData->light->OnLightDataInvalided, [=](Light*)
{
//TODO: Switch lights to storage buffers so they can all be part of GPU memory
for (auto&& [viewer, viewerData] : m_viewers)
for (auto& viewerData : m_viewerPool)
{
UInt32 viewerRenderMask = viewer->GetRenderMask();
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
if (viewerRenderMask & renderMask)
viewerData.forwardPass->ForceInvalidation();
}
});
return lightIndex;
}
void ForwardFramePipeline::RegisterMaterialPass(MaterialPass* materialPass)
@@ -156,16 +88,100 @@ namespace Nz
it->second.usedCount++;
}
void ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder)
std::size_t ForwardFramePipeline::RegisterRenderable(std::size_t worldInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox)
{
auto& viewerData = m_viewers.emplace(viewerInstance, ViewerData{}).first->second;
std::size_t renderableIndex;
RenderableData* renderableData = m_renderablePool.Allocate(renderableIndex);
renderableData->renderable = instancedRenderable;
renderableData->renderMask = renderMask;
renderableData->scissorBox = scissorBox;
renderableData->worldInstanceIndex = worldInstanceIndex;
renderableData->onElementInvalidated.Connect(instancedRenderable->OnElementInvalidated, [=](InstancedRenderable* /*instancedRenderable*/)
{
// TODO: Invalidate only relevant viewers and passes
for (auto& viewerData : m_viewerPool)
{
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
if (viewerRenderMask & renderMask)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->ForceInvalidation();
viewerData.forwardPass->ForceInvalidation();
}
}
});
renderableData->onMaterialInvalidated.Connect(instancedRenderable->OnMaterialInvalidated, [this](InstancedRenderable* instancedRenderable, std::size_t materialIndex, const std::shared_ptr<Material>& newMaterial)
{
if (newMaterial)
{
for (auto& viewerData : m_viewerPool)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterMaterial(*newMaterial);
viewerData.forwardPass->RegisterMaterial(*newMaterial);
}
}
const auto& prevMaterial = instancedRenderable->GetMaterial(materialIndex);
if (prevMaterial)
{
for (auto& viewerData : m_viewerPool)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->UnregisterMaterial(*prevMaterial);
viewerData.forwardPass->UnregisterMaterial(*prevMaterial);
}
}
});
std::size_t matCount = instancedRenderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
if (Material* mat = instancedRenderable->GetMaterial(i).get())
{
for (auto& viewerData : m_viewerPool)
{
if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterMaterial(*mat);
viewerData.forwardPass->RegisterMaterial(*mat);
}
}
}
return renderableIndex;
}
std::size_t ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder)
{
std::size_t viewerIndex;
auto& viewerData = *m_viewerPool.Allocate(viewerIndex);
viewerData.renderOrder = renderOrder;
viewerData.depthPrepass = std::make_unique<DepthPipelinePass>(*this, viewerInstance);
viewerData.forwardPass = std::make_unique<ForwardPipelinePass>(*this, viewerInstance);
viewerData.viewer = viewerInstance;
m_invalidatedViewerInstances.insert(viewerInstance);
m_invalidatedViewerInstances.UnboundedSet(viewerIndex);
m_rebuildFrameGraph = true;
return viewerIndex;
}
std::size_t ForwardFramePipeline::RegisterWorldInstance(WorldInstancePtr worldInstance)
{
std::size_t worldInstanceIndex;
m_worldInstances.Allocate(worldInstanceIndex, std::move(worldInstance));
m_invalidatedWorldInstances.UnboundedSet(worldInstanceIndex);
return worldInstanceIndex;
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
@@ -174,8 +190,13 @@ namespace Nz
Graphics* graphics = Graphics::Instance();
renderFrame.PushForRelease(std::move(m_removedWorldInstances));
m_removedWorldInstances.clear();
// Destroy world instances at the end of the frame
for (std::size_t worldInstanceIndex = m_removedWorldInstances.FindFirst(); worldInstanceIndex != m_removedWorldInstances.npos; worldInstanceIndex = m_removedWorldInstances.FindNext(worldInstanceIndex))
{
renderFrame.PushForRelease(*m_worldInstances.RetrieveFromIndex(worldInstanceIndex));
m_worldInstances.Free(worldInstanceIndex);
}
m_removedWorldInstances.Clear();
if (m_rebuildFrameGraph)
{
@@ -192,15 +213,19 @@ namespace Nz
{
builder.PreTransferBarrier();
for (AbstractViewer* viewer : m_invalidatedViewerInstances)
viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder);
for (std::size_t viewerIndex = m_invalidatedViewerInstances.FindFirst(); viewerIndex != m_invalidatedViewerInstances.npos; viewerIndex = m_invalidatedViewerInstances.FindNext(viewerIndex))
{
ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex);
viewerData->viewer->GetViewerInstance().UpdateBuffers(uploadPool, builder);
}
m_invalidatedViewerInstances.Reset();
m_invalidatedViewerInstances.clear();
for (WorldInstance* worldInstance : m_invalidatedWorldInstances)
for (std::size_t worldInstanceIndex = m_invalidatedWorldInstances.FindFirst(); worldInstanceIndex != m_invalidatedWorldInstances.npos; worldInstanceIndex = m_invalidatedWorldInstances.FindNext(worldInstanceIndex))
{
WorldInstancePtr& worldInstance = *m_worldInstances.RetrieveFromIndex(worldInstanceIndex);
worldInstance->UpdateBuffers(uploadPool, builder);
m_invalidatedWorldInstances.clear();
}
m_invalidatedWorldInstances.Reset();
for (MaterialPass* materialPass : m_invalidatedMaterialPasses)
materialPass->Update(renderFrame, builder);
@@ -218,61 +243,53 @@ namespace Nz
};
// Render queues handling
for (auto&& [viewer, data] : m_viewers)
for (auto& viewerData : m_viewerPool)
{
auto& viewerData = data;
UInt32 renderMask = viewer->GetRenderMask();
UInt32 renderMask = viewerData.viewer->GetRenderMask();
// Frustum culling
const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix();
const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix();
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
std::size_t visibilityHash = 5U;
m_visibleRenderables.clear();
for (const auto& [worldInstance, renderables] : m_renderables)
for (const RenderableData& renderableData : m_renderablePool)
{
bool isInstanceVisible = false;
if ((renderMask & renderableData.renderMask) == 0)
continue;
for (const auto& [renderable, renderableData] : renderables)
{
if ((renderMask & renderableData.renderMask) == 0)
continue;
WorldInstancePtr& worldInstance = *m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex);
// Get global AABB
BoundingVolumef boundingVolume(renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
// Get global AABB
BoundingVolumef boundingVolume(renderableData.renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
if (!frustum.Contains(boundingVolume))
continue;
if (!frustum.Contains(boundingVolume))
continue;
auto& visibleRenderable = m_visibleRenderables.emplace_back();
visibleRenderable.instancedRenderable = renderable;
visibleRenderable.worldInstance = worldInstance.get();
auto& visibleRenderable = m_visibleRenderables.emplace_back();
visibleRenderable.instancedRenderable = renderableData.renderable;
visibleRenderable.scissorBox = renderableData.scissorBox;
visibleRenderable.worldInstance = worldInstance.get();
isInstanceVisible = true;
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(renderable));
}
if (isInstanceVisible)
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(worldInstance.get()));
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(&renderableData));
}
// Lights update don't trigger a rebuild of the depth pre-pass
std::size_t depthVisibilityHash = visibilityHash;
m_visibleLights.clear();
for (auto&& [light, lightData] : m_lights)
for (const LightData& lightData : m_lightPool)
{
const BoundingVolumef& boundingVolume = light->GetBoundingVolume();
const BoundingVolumef& boundingVolume = lightData.light->GetBoundingVolume();
// TODO: Use more precise tests for point lights (frustum/sphere is cheap)
if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume))
{
m_visibleLights.push_back(light);
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(light));
m_visibleLights.push_back(lightData.light.get());
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(lightData.light.get()));
}
}
@@ -285,7 +302,7 @@ namespace Nz
if (m_bakedFrameGraph.Resize(renderFrame))
{
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
for (auto&& [_, viewerData] : m_viewers)
for (auto& viewerData : m_viewerPool)
{
if (viewerData.blitShaderBinding)
renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding));
@@ -362,43 +379,9 @@ namespace Nz
}
}
void ForwardFramePipeline::UnregisterInstancedDrawable(const WorldInstancePtr& worldInstance, const InstancedRenderable* instancedRenderable)
void ForwardFramePipeline::UnregisterLight(std::size_t lightIndex)
{
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)
{
for (auto&& [viewer, viewerData] : m_viewers)
{
const auto& material = instancedRenderable->GetMaterial(i);
if (viewerData.depthPrepass)
viewerData.depthPrepass->UnregisterMaterial(*material);
viewerData.forwardPass->UnregisterMaterial(*material);
}
}
}
void ForwardFramePipeline::UnregisterLight(Light* light)
{
m_lights.erase(light);
m_lightPool.Free(lightIndex);
}
void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* materialPass)
@@ -412,17 +395,72 @@ namespace Nz
m_activeMaterialPasses.erase(materialPass);
}
void ForwardFramePipeline::UnregisterViewer(AbstractViewer* viewerInstance)
void ForwardFramePipeline::UnregisterRenderable(std::size_t renderableIndex)
{
m_viewers.erase(viewerInstance);
RenderableData& renderable = *m_renderablePool.RetrieveFromIndex(renderableIndex);
std::size_t matCount = renderable.renderable->GetMaterialCount();
for (std::size_t i = 0; i < matCount; ++i)
{
for (auto& viewerData : m_viewerPool)
{
const auto& material = renderable.renderable->GetMaterial(i);
if (viewerData.depthPrepass)
viewerData.depthPrepass->UnregisterMaterial(*material);
viewerData.forwardPass->UnregisterMaterial(*material);
}
}
m_renderablePool.Free(renderableIndex);
}
void ForwardFramePipeline::UnregisterViewer(std::size_t viewerIndex)
{
m_viewerPool.Free(viewerIndex);
m_invalidatedViewerInstances.Reset(viewerIndex);
m_rebuildFrameGraph = true;
}
void ForwardFramePipeline::UnregisterWorldInstance(std::size_t worldInstance)
{
// Defer world instance release
m_removedWorldInstances.UnboundedSet(worldInstance);
}
void ForwardFramePipeline::UpdateLightRenderMask(std::size_t lightIndex, UInt32 renderMask)
{
LightData* lightData = m_lightPool.RetrieveFromIndex(lightIndex);
lightData->renderMask = renderMask;
}
void ForwardFramePipeline::UpdateRenderableRenderMask(std::size_t renderableIndex, UInt32 renderMask)
{
RenderableData* renderableData = m_renderablePool.RetrieveFromIndex(renderableIndex);
renderableData->renderMask = renderMask;
}
void ForwardFramePipeline::UpdateRenderableScissorBox(std::size_t renderableIndex, const Recti& scissorBox)
{
RenderableData* renderableData = m_renderablePool.RetrieveFromIndex(renderableIndex);
renderableData->scissorBox = scissorBox;
}
void ForwardFramePipeline::UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder)
{
ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex);
if (viewerData->renderOrder != renderOrder)
{
viewerData->renderOrder = renderOrder;
m_rebuildFrameGraph = true;
}
}
BakedFrameGraph ForwardFramePipeline::BuildFrameGraph()
{
FrameGraph frameGraph;
for (auto&& [viewer, viewerData] : m_viewers)
for (auto& viewerData : m_viewerPool)
{
viewerData.colorAttachment = frameGraph.AddAttachment({
"Color",
@@ -433,26 +471,21 @@ namespace Nz
"Depth-stencil buffer",
Graphics::Instance()->GetPreferredDepthStencilFormat()
});
}
for (auto&& [viewer, data] : m_viewers)
{
auto& viewerData = data;
if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterToFrameGraph(frameGraph, viewerData.depthStencilAttachment);
viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.colorAttachment, viewerData.depthStencilAttachment);
viewerData.forwardPass->RegisterToFrameGraph(frameGraph, viewerData.colorAttachment, viewerData.depthStencilAttachment, viewerData.depthPrepass != nullptr);
}
using ViewerPair = std::pair<const RenderTarget*, const ViewerData*>;
StackArray<ViewerPair> viewers = NazaraStackArray(ViewerPair, m_viewers.size());
StackArray<ViewerPair> viewers = NazaraStackArray(ViewerPair, m_viewerPool.size());
auto viewerIt = viewers.begin();
for (auto&& [viewer, viewerData] : m_viewers)
for (auto& viewerData : m_viewerPool)
{
const RenderTarget& renderTarget = viewer->GetRenderTarget();
const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget();
*viewerIt++ = std::make_pair(&renderTarget, &viewerData);
}

View File

@@ -265,11 +265,14 @@ namespace Nz
it->second.usedCount++;
}
void ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex)
void ForwardPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass)
{
FramePass& forwardPass = frameGraph.AddPass("Forward pass");
forwardPass.AddOutput(colorBufferIndex);
forwardPass.SetDepthStencilInput(depthBufferIndex);
if (hasDepthPrepass)
forwardPass.SetDepthStencilInput(depthBufferIndex);
else
forwardPass.SetDepthStencilOutput(depthBufferIndex);
forwardPass.SetClearColor(0, m_viewer->GetClearColor());
forwardPass.SetDepthStencilClear(1.f, 0);

View File

@@ -19,7 +19,10 @@ namespace Nz
RenderSystem::RenderSystem(entt::registry& registry) :
m_cameraConstructObserver(registry, entt::collector.group<CameraComponent, NodeComponent>()),
m_graphicsConstructObserver(registry, entt::collector.group<GraphicsComponent, NodeComponent>()),
m_lightConstructObserver(registry, entt::collector.group<LightComponent, NodeComponent>())
m_lightConstructObserver(registry, entt::collector.group<LightComponent, NodeComponent>()),
m_cameraEntityPool(8),
m_graphicsEntityPool(1024),
m_lightEntityPool(32)
{
m_cameraDestroyConnection = registry.on_destroy<CameraComponent>().connect<&RenderSystem::OnCameraDestroy>(this);
m_graphicsDestroyConnection = registry.on_destroy<GraphicsComponent>().connect<&RenderSystem::OnGraphicsDestroy>(this);
@@ -47,16 +50,20 @@ namespace Nz
CameraComponent& entityCamera = registry.get<CameraComponent>(entity);
NodeComponent& entityNode = registry.get<NodeComponent>(entity);
m_pipeline->RegisterViewer(&entityCamera, entityCamera.GetRenderOrder());
std::size_t poolIndex;
CameraEntity* cameraEntity = m_cameraEntityPool.Allocate(poolIndex);
cameraEntity->poolIndex = poolIndex;
cameraEntity->entity = entity;
cameraEntity->viewerIndex = m_pipeline->RegisterViewer(&entityCamera, entityCamera.GetRenderOrder());
cameraEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, cameraEntity](const Node* /*node*/)
{
m_invalidatedCameraNode.insert(cameraEntity);
});
m_invalidatedCameraNode.insert(entity);
m_invalidatedCameraNode.insert(cameraEntity);
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_cameraEntities.emplace(entity, cameraEntity);
});
m_graphicsConstructObserver.each([&](entt::entity entity)
@@ -64,53 +71,77 @@ namespace Nz
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
NodeComponent& entityNode = registry.get<NodeComponent>(entity);
if (entityGfx.IsVisible())
std::size_t poolIndex;
GraphicsEntity* graphicsEntity = m_graphicsEntityPool.Allocate(poolIndex);
graphicsEntity->entity = entity;
graphicsEntity->poolIndex = poolIndex;
graphicsEntity->worldInstanceIndex = m_pipeline->RegisterWorldInstance(entityGfx.GetWorldInstance());
graphicsEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, graphicsEntity](const Node* /*node*/)
{
const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance();
for (const auto& renderableEntry : entityGfx.GetRenderables())
m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask);
}
m_invalidatedGfxWorldNode.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_invalidatedGfxWorldNode.insert(entity);
m_invalidatedGfxWorldNode.insert(graphicsEntity);
});
graphicsEntity.onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry)
graphicsEntity->onRenderableAttached.Connect(entityGfx.OnRenderableAttached, [this, graphicsEntity](GraphicsComponent* gfx, std::size_t renderableIndex)
{
if (!gfx->IsVisible())
return;
const WorldInstancePtr& worldInstance = gfx->GetWorldInstance();
m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask);
const auto& renderableEntry = gfx->GetRenderableEntry(renderableIndex);
graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, gfx->GetScissorBox());
});
graphicsEntity.onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this](GraphicsComponent* gfx, const GraphicsComponent::Renderable& renderableEntry)
graphicsEntity->onRenderableDetach.Connect(entityGfx.OnRenderableDetach, [this, graphicsEntity](GraphicsComponent* gfx, std::size_t renderableIndex)
{
if (!gfx->IsVisible())
return;
const WorldInstancePtr& worldInstance = gfx->GetWorldInstance();
m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get());
m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]);
});
graphicsEntity.onVisibilityUpdate.Connect(entityGfx.OnVisibilityUpdate, [this, entity](GraphicsComponent* /*gfx*/, bool isVisible)
graphicsEntity->onScissorBoxUpdate.Connect(entityGfx.OnScissorBoxUpdate, [this, graphicsEntity](GraphicsComponent* gfx, const Recti& scissorBox)
{
if (!gfx->IsVisible())
return;
for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex)
{
const auto& renderableEntry = gfx->GetRenderableEntry(renderableIndex);
if (!renderableEntry.renderable)
continue;
m_pipeline->UpdateRenderableScissorBox(graphicsEntity->renderableIndices[renderableIndex], scissorBox);
}
});
graphicsEntity->onVisibilityUpdate.Connect(entityGfx.OnVisibilityUpdate, [this, graphicsEntity](GraphicsComponent* /*gfx*/, bool isVisible)
{
if (isVisible)
{
m_newlyHiddenGfxEntities.erase(entity);
m_newlyVisibleGfxEntities.insert(entity);
m_newlyHiddenGfxEntities.erase(graphicsEntity);
m_newlyVisibleGfxEntities.insert(graphicsEntity);
}
else
{
m_newlyHiddenGfxEntities.insert(entity);
m_newlyVisibleGfxEntities.erase(entity);
m_newlyHiddenGfxEntities.insert(graphicsEntity);
m_newlyVisibleGfxEntities.erase(graphicsEntity);
}
});
m_invalidatedGfxWorldNode.insert(graphicsEntity);
if (entityGfx.IsVisible())
{
for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex)
{
const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex);
if (!renderableEntry.renderable)
continue;
graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox());
}
}
assert(m_graphicsEntities.find(entity) == m_graphicsEntities.end());
m_graphicsEntities.emplace(entity, graphicsEntity);
});
m_lightConstructObserver.each([&](entt::entity entity)
@@ -118,50 +149,67 @@ namespace Nz
LightComponent& entityLight = registry.get<LightComponent>(entity);
NodeComponent& entityNode = registry.get<NodeComponent>(entity);
if (entityLight.IsVisible())
std::size_t poolIndex;
LightEntity* lightEntity = m_lightEntityPool.Allocate(poolIndex);
lightEntity->entity = entity;
lightEntity->poolIndex = poolIndex;
lightEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, lightEntity](const Node* /*node*/)
{
for (const auto& lightEntry : entityLight.GetLights())
m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
}
m_invalidatedLightWorldNode.insert(entity);
assert(m_lightEntities.find(entity) == m_lightEntities.end());
auto& lightEntity = m_lightEntities[entity];
lightEntity.onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, entity](const Node* /*node*/)
{
m_invalidatedLightWorldNode.insert(entity);
m_invalidatedLightWorldNode.insert(lightEntity);
});
lightEntity.onLightAttached.Connect(entityLight.OnLightAttached, [this](LightComponent* light, const LightComponent::LightEntry& lightEntry)
lightEntity->onNodeInvalidation.Connect(entityNode.OnNodeInvalidation, [this, lightEntity](const Node* /*node*/)
{
m_invalidatedLightWorldNode.insert(lightEntity);
});
lightEntity->onLightAttached.Connect(entityLight.OnLightAttached, [this, lightEntity](LightComponent* light, std::size_t lightIndex)
{
if (!light->IsVisible())
return;
m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
const auto& lightEntry = light->GetLightEntry(lightIndex);
lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
});
lightEntity.onLightDetach.Connect(entityLight.OnLightDetach, [this](LightComponent* light, const LightComponent::LightEntry& lightEntry)
lightEntity->onLightDetach.Connect(entityLight.OnLightDetach, [this, lightEntity](LightComponent* light, std::size_t lightIndex)
{
if (!light->IsVisible())
return;
m_pipeline->UnregisterLight(lightEntry.light.get());
m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]);
});
lightEntity.onVisibilityUpdate.Connect(entityLight.OnVisibilityUpdate, [this, entity](LightComponent* /*light*/, bool isVisible)
lightEntity->onVisibilityUpdate.Connect(entityLight.OnVisibilityUpdate, [this, lightEntity](LightComponent* /*light*/, bool isVisible)
{
if (isVisible)
{
m_newlyHiddenLightEntities.erase(entity);
m_newlyVisibleLightEntities.insert(entity);
m_newlyHiddenLightEntities.erase(lightEntity);
m_newlyVisibleLightEntities.insert(lightEntity);
}
else
{
m_newlyHiddenLightEntities.insert(entity);
m_newlyVisibleLightEntities.erase(entity);
m_newlyHiddenLightEntities.insert(lightEntity);
m_newlyVisibleLightEntities.erase(lightEntity);
}
});
m_invalidatedLightWorldNode.insert(lightEntity);
if (entityLight.IsVisible())
{
for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex)
{
const auto& lightEntry = entityLight.GetLightEntry(lightIndex);
if (!lightEntry.light)
continue;
lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
}
}
assert(m_lightEntities.find(entity) == m_lightEntities.end());
m_lightEntities.emplace(entity, lightEntity);
});
UpdateVisibility(registry);
@@ -172,45 +220,74 @@ namespace Nz
void RenderSystem::OnCameraDestroy(entt::registry& registry, entt::entity entity)
{
m_cameraEntities.erase(entity);
m_invalidatedCameraNode.erase(entity);
auto it = m_cameraEntities.find(entity);
if (it == m_cameraEntities.end())
return;
CameraComponent& entityCamera = registry.get<CameraComponent>(entity);
m_pipeline->UnregisterViewer(&entityCamera);
CameraEntity* cameraEntity = it->second;
m_cameraEntities.erase(it);
m_invalidatedCameraNode.erase(cameraEntity);
m_pipeline->UnregisterViewer(cameraEntity->viewerIndex);
m_cameraEntityPool.Free(cameraEntity->poolIndex);
}
void RenderSystem::OnGraphicsDestroy(entt::registry& registry, entt::entity entity)
{
auto it = m_graphicsEntities.find(entity);
if (it == m_graphicsEntities.end())
return;
GraphicsEntity* graphicsEntity = it->second;
m_graphicsEntities.erase(entity);
m_invalidatedGfxWorldNode.erase(entity);
m_newlyHiddenGfxEntities.erase(entity);
m_newlyVisibleGfxEntities.erase(entity);
m_invalidatedGfxWorldNode.erase(graphicsEntity);
m_newlyHiddenGfxEntities.erase(graphicsEntity);
m_newlyVisibleGfxEntities.erase(graphicsEntity);
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance();
for (const auto& renderableEntry : entityGfx.GetRenderables())
m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get());
for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex)
{
const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex);
if (!renderableEntry.renderable)
continue;
m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]);
}
m_pipeline->UnregisterWorldInstance(graphicsEntity->worldInstanceIndex);
m_graphicsEntityPool.Free(graphicsEntity->poolIndex);
}
void RenderSystem::OnLightDestroy(entt::registry& registry, entt::entity entity)
{
auto it = m_lightEntities.find(entity);
if (it == m_lightEntities.end())
return;
LightEntity* lightEntity = it->second;
m_lightEntities.erase(entity);
m_invalidatedLightWorldNode.erase(entity);
m_newlyHiddenLightEntities.erase(entity);
m_newlyVisibleLightEntities.erase(entity);
m_invalidatedLightWorldNode.erase(lightEntity);
m_newlyHiddenLightEntities.erase(lightEntity);
m_newlyVisibleLightEntities.erase(lightEntity);
LightComponent& entityLight = registry.get<LightComponent>(entity);
for (const auto& lightEntry : entityLight.GetLights())
m_pipeline->UnregisterLight(lightEntry.light.get());
for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex)
{
const auto& lightEntry = entityLight.GetLightEntry(lightIndex);
if (!lightEntry.light)
continue;
m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]);
}
m_lightEntityPool.Free(lightEntity->poolIndex);
}
void RenderSystem::OnNodeDestroy(entt::registry& registry, entt::entity entity)
{
m_newlyHiddenGfxEntities.erase(entity);
m_newlyVisibleGfxEntities.erase(entity);
m_newlyHiddenLightEntities.erase(entity);
m_newlyVisibleLightEntities.erase(entity);
if (registry.try_get<CameraComponent>(entity))
OnCameraDestroy(registry, entity);
@@ -223,8 +300,10 @@ namespace Nz
void RenderSystem::UpdateInstances(entt::registry& registry)
{
for (entt::entity entity : m_invalidatedCameraNode)
for (CameraEntity* cameraEntity : m_invalidatedCameraNode)
{
entt::entity entity = cameraEntity->entity;
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
CameraComponent& entityCamera = registry.get<CameraComponent>(entity);
@@ -234,24 +313,28 @@ namespace Nz
viewerInstance.UpdateEyePosition(cameraPosition);
viewerInstance.UpdateViewMatrix(Nz::Matrix4f::ViewMatrix(cameraPosition, entityNode.GetRotation(CoordSys::Global)));
m_pipeline->InvalidateViewer(&entityCamera);
m_pipeline->InvalidateViewer(cameraEntity->viewerIndex);
}
m_invalidatedCameraNode.clear();
for (entt::entity entity : m_invalidatedGfxWorldNode)
for (GraphicsEntity* graphicsEntity : m_invalidatedGfxWorldNode)
{
entt::entity entity = graphicsEntity->entity;
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
GraphicsComponent& entityGraphics = registry.get<GraphicsComponent>(entity);
const WorldInstancePtr& worldInstance = entityGraphics.GetWorldInstance();
worldInstance->UpdateWorldMatrix(entityNode.GetTransformMatrix());
m_pipeline->InvalidateWorldInstance(worldInstance.get());
m_pipeline->InvalidateWorldInstance(graphicsEntity->worldInstanceIndex);
}
m_invalidatedGfxWorldNode.clear();
for (entt::entity entity : m_invalidatedLightWorldNode)
for (LightEntity* lightEntity : m_invalidatedLightWorldNode)
{
entt::entity entity = lightEntity->entity;
const NodeComponent& entityNode = registry.get<const NodeComponent>(entity);
LightComponent& entityLight = registry.get<LightComponent>(entity);
@@ -260,7 +343,12 @@ namespace Nz
const Vector3f& scale = entityNode.GetScale(CoordSys::Global);
for (const auto& lightEntry : entityLight.GetLights())
{
if (!lightEntry.light)
continue;
lightEntry.light->UpdateTransform(position, rotation, scale);
}
}
m_invalidatedLightWorldNode.clear();
}
@@ -268,25 +356,69 @@ namespace Nz
void RenderSystem::UpdateVisibility(entt::registry& registry)
{
// Unregister drawable for hidden entities
for (entt::entity entity : m_newlyHiddenGfxEntities)
for (GraphicsEntity* graphicsEntity : m_newlyHiddenGfxEntities)
{
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(graphicsEntity->entity);
const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance();
for (const auto& renderableEntry : entityGfx.GetRenderables())
m_pipeline->UnregisterInstancedDrawable(worldInstance, renderableEntry.renderable.get());
for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex)
{
const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex);
if (!renderableEntry.renderable)
continue;
m_pipeline->UnregisterRenderable(graphicsEntity->renderableIndices[renderableIndex]);
}
}
m_newlyHiddenGfxEntities.clear();
// Register drawable for newly visible entities
for (entt::entity entity : m_newlyVisibleGfxEntities)
for (GraphicsEntity* graphicsEntity : m_newlyVisibleGfxEntities)
{
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(entity);
GraphicsComponent& entityGfx = registry.get<GraphicsComponent>(graphicsEntity->entity);
const WorldInstancePtr& worldInstance = entityGfx.GetWorldInstance();
for (const auto& renderableEntry : entityGfx.GetRenderables())
m_pipeline->RegisterInstancedDrawable(worldInstance, renderableEntry.renderable.get(), renderableEntry.renderMask);
for (std::size_t renderableIndex = 0; renderableIndex < GraphicsComponent::MaxRenderableCount; ++renderableIndex)
{
const auto& renderableEntry = entityGfx.GetRenderableEntry(renderableIndex);
if (!renderableEntry.renderable)
continue;
graphicsEntity->renderableIndices[renderableIndex] = m_pipeline->RegisterRenderable(graphicsEntity->worldInstanceIndex, renderableEntry.renderable.get(), renderableEntry.renderMask, entityGfx.GetScissorBox());
}
}
m_newlyVisibleGfxEntities.clear();
// Unregister lights for hidden entities
for (LightEntity* lightEntity : m_newlyHiddenLightEntities)
{
LightComponent& entityLights = registry.get<LightComponent>(lightEntity->entity);
for (std::size_t lightIndex = 0; lightIndex < LightComponent::MaxLightCount; ++lightIndex)
{
const auto& lightEntry = entityLights.GetLightEntry(lightIndex);
if (!lightEntry.light)
continue;
m_pipeline->UnregisterLight(lightEntity->lightIndices[lightIndex]);
}
}
m_newlyHiddenGfxEntities.clear();
// Register lights for newly visible entities
for (LightEntity* lightEntity : m_newlyVisibleLightEntities)
{
LightComponent& entityLights = registry.get<LightComponent>(lightEntity->entity);
for (std::size_t renderableIndex = 0; renderableIndex < LightComponent::MaxLightCount; ++renderableIndex)
{
const auto& lightEntry = entityLights.GetLightEntry(renderableIndex);
if (!lightEntry.light)
continue;
lightEntity->lightIndices[renderableIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
}
}
m_newlyVisibleGfxEntities.clear();
//FIXME: Handle light visibility
}
}