浏览代码

The render queue is feature complete

Panagiotis Christopoulos Charitos 8 年之前
父节点
当前提交
11a0c09adf
共有 42 个文件被更改,包括 705 次插入1092 次删除
  1. 4 1
      src/anki/core/App.cpp
  2. 1 4
      src/anki/renderer/Clusterer.cpp
  3. 0 2
      src/anki/renderer/Clusterer.h
  4. 3 79
      src/anki/renderer/Dbg.cpp
  5. 8 98
      src/anki/renderer/DebugDrawer.cpp
  6. 4 10
      src/anki/renderer/DebugDrawer.h
  7. 7 13
      src/anki/renderer/Drawer.cpp
  8. 3 3
      src/anki/renderer/Drawer.h
  9. 0 2
      src/anki/renderer/FinalComposite.cpp
  10. 9 9
      src/anki/renderer/ForwardShading.cpp
  11. 5 8
      src/anki/renderer/GBuffer.cpp
  12. 64 96
      src/anki/renderer/Indirect.cpp
  13. 4 8
      src/anki/renderer/Indirect.h
  14. 16 23
      src/anki/renderer/LensFlare.cpp
  15. 95 121
      src/anki/renderer/LightBin.cpp
  16. 8 16
      src/anki/renderer/LightBin.h
  17. 11 9
      src/anki/renderer/LightShading.cpp
  18. 0 1
      src/anki/renderer/LightShading.h
  19. 4 12
      src/anki/renderer/MainRenderer.cpp
  20. 1 3
      src/anki/renderer/MainRenderer.h
  21. 6 2
      src/anki/renderer/RenderQueue.h
  22. 14 19
      src/anki/renderer/Renderer.cpp
  23. 6 14
      src/anki/renderer/Renderer.h
  24. 92 91
      src/anki/renderer/ShadowMapping.cpp
  25. 17 12
      src/anki/renderer/ShadowMapping.h
  26. 4 4
      src/anki/renderer/Ssao.cpp
  27. 5 4
      src/anki/renderer/Volumetric.cpp
  28. 0 1
      src/anki/scene/Forward.h
  29. 0 11
      src/anki/scene/FrustumComponent.cpp
  30. 0 33
      src/anki/scene/FrustumComponent.h
  31. 25 4
      src/anki/scene/Light.cpp
  32. 5 3
      src/anki/scene/Light.h
  33. 16 4
      src/anki/scene/LightComponent.cpp
  34. 24 40
      src/anki/scene/LightComponent.h
  35. 0 3
      src/anki/scene/ReflectionProbeComponent.h
  36. 1 1
      src/anki/scene/SceneComponent.h
  37. 12 6
      src/anki/scene/SceneGraph.cpp
  38. 2 0
      src/anki/scene/SceneGraph.h
  39. 9 1
      src/anki/scene/SceneNode.h
  40. 165 160
      src/anki/scene/Visibility.cpp
  41. 2 145
      src/anki/scene/Visibility.h
  42. 53 16
      src/anki/scene/VisibilityInternal.h

+ 4 - 1
src/anki/core/App.cpp

@@ -397,7 +397,10 @@ Error App::mainLoop()
 
 		ANKI_CHECK(m_scene->update(prevUpdateTime, crntTime));
 
-		ANKI_CHECK(m_renderer->render(*m_scene));
+		RenderQueue rqueue;
+		m_scene->doVisibilityTests(rqueue);
+
+		ANKI_CHECK(m_renderer->render(rqueue));
 
 		// Pause and sync async loader. That will force all tasks before the pause to finish in this frame.
 		m_resources->getAsyncLoader().pause();

+ 1 - 4
src/anki/renderer/Clusterer.cpp

@@ -4,11 +4,8 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/Clusterer.h>
-#include <anki/scene/FrustumComponent.h>
-#include <anki/scene/MoveComponent.h>
-#include <anki/scene/SceneNode.h>
 #include <anki/util/ThreadPool.h>
-#include <vector>
+#include <anki/Collision.h>
 
 namespace anki
 {

+ 0 - 2
src/anki/renderer/Clusterer.h

@@ -14,8 +14,6 @@ namespace anki
 {
 
 // Forward
-class FrustumComponent;
-class SceneNode;
 class ThreadPool;
 class PerspectiveFrustum;
 

+ 3 - 79
src/anki/renderer/Dbg.cpp

@@ -85,91 +85,15 @@ Error Dbg::run(RenderingContext& ctx)
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
 	m_drawer->prepareFrame(cmdb);
-	m_drawer->setViewProjectionMatrix(ctx.m_viewProjMat);
+	m_drawer->setViewProjectionMatrix(ctx.m_renderQueue->m_viewProjectionMatrix);
 	m_drawer->setModelMatrix(Mat4::getIdentity());
 	// m_drawer->drawGrid();
 
-	const SceneGraph* scene = nullptr;
-
 	SceneDebugDrawer sceneDrawer(m_drawer);
-	ctx.m_visResults->iterateAll([&](const SceneNode& node) {
-		// Get the scenegraph
-		if(scene == nullptr)
-		{
-			scene = &node.getSceneGraph();
-		}
 
-		/*if(&node == &cam)
-		{
-			return;
-		}*/
-
-		// Set position
-		const MoveComponent* mv = node.tryGetComponent<MoveComponent>();
-		if(mv)
-		{
-			m_drawer->setModelMatrix(Mat4(mv->getWorldTransform()));
-		}
-		else
-		{
-			m_drawer->setModelMatrix(Mat4::getIdentity());
-		}
-
-		// Spatial
-		if(m_flags.get(DbgFlag::SPATIAL_COMPONENT))
-		{
-			Error err = node.iterateComponentsOfType<const SpatialComponent>([&](const SpatialComponent& sp) -> Error {
-				sceneDrawer.draw(sp);
-				return ErrorCode::NONE;
-			});
-			(void)err;
-		}
-
-		// Frustum
-		if(m_flags.get(DbgFlag::FRUSTUM_COMPONENT))
-		{
-			Error err = node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
-				/*if(&frc != &camFrc)
-				{
-					sceneDrawer.draw(frc);
-				}*/
-				return ErrorCode::NONE;
-			});
-			(void)err;
-		}
-
-		// Sector/portal
-		if(m_flags.get(DbgFlag::SECTOR_COMPONENT))
-		{
-			Error err = node.iterateComponentsOfType<SectorComponent>([&](SectorComponent& psc) -> Error {
-				sceneDrawer.draw(psc);
-				return ErrorCode::NONE;
-			});
-
-			err = node.iterateComponentsOfType<PortalComponent>([&](PortalComponent& psc) -> Error {
-				sceneDrawer.draw(psc);
-				return ErrorCode::NONE;
-			});
-			(void)err;
-		}
-
-		// Decal
-		if(m_flags.get(DbgFlag::DECAL_COMPONENT))
-		{
-			Error err = node.iterateComponentsOfType<DecalComponent>([&](DecalComponent& psc) -> Error {
-				sceneDrawer.draw(psc);
-				return ErrorCode::NONE;
-			});
-			(void)err;
-		}
-	});
-
-	if(m_flags.get(DbgFlag::PHYSICS) && scene)
+	for(const PointLightQueueElement& plight : ctx.m_renderQueue->m_pointLights)
 	{
-		PhysicsDebugDrawer phyd(m_drawer);
-
-		m_drawer->setModelMatrix(Mat4::getIdentity());
-		phyd.drawWorld(scene->getPhysicsWorld());
+		sceneDrawer.draw(plight);
 	}
 
 #if 0

+ 8 - 98
src/anki/renderer/DebugDrawer.cpp

@@ -418,112 +418,22 @@ void PhysicsDebugDrawer::drawLines(const Vec3* lines, const U32 linesCount, cons
 	m_dbg->end();
 }
 
-void SceneDebugDrawer::draw(const FrustumComponent& fr) const
+void SceneDebugDrawer::draw(const RenderableQueueElement& r) const
 {
-	const Frustum& fs = fr.getFrustum();
-
-	m_dbg->setColor(Vec3(1.0, 1.0, 0.0));
-	CollisionDebugDrawer coldraw(m_dbg);
-	fs.accept(coldraw);
+	// TODO
 }
 
-void SceneDebugDrawer::draw(const SpatialComponent& x) const
+void SceneDebugDrawer::draw(const PointLightQueueElement& light) const
 {
-	if(!x.getVisibleByCamera())
-	{
-		return;
-	}
-
-	m_dbg->setColor(Vec3(1.0, 0.0, 1.0));
+	m_dbg->setColor(light.m_diffuseColor);
 	CollisionDebugDrawer coldraw(m_dbg);
-	x.getAabb().accept(coldraw);
+	Sphere sphere(light.m_worldPosition.xyz0(), light.m_radius);
+	sphere.accept(coldraw);
 }
 
-void SceneDebugDrawer::draw(const SectorComponent& c) const
+void SceneDebugDrawer::draw(const SpotLightQueueElement& light) const
 {
-	const SceneNode& node = c.getSceneNode();
-	const PortalSectorBase& psnode = static_cast<const PortalSectorBase&>(node);
-
-	m_dbg->setColor(Vec3(0.0, 0.0, 1.0));
-
-	m_dbg->begin(PrimitiveTopology::LINES);
-	const auto& verts = psnode.getVertices();
-	ANKI_ASSERT((psnode.getVertexIndices().getSize() % 3) == 0);
-	for(U i = 0; i < psnode.getVertexIndices().getSize(); i += 3)
-	{
-		I id0 = psnode.getVertexIndices()[i];
-		I id1 = psnode.getVertexIndices()[i + 1];
-		I id2 = psnode.getVertexIndices()[i + 2];
-
-		m_dbg->pushBackVertex(verts[id0].xyz());
-		m_dbg->pushBackVertex(verts[id1].xyz());
-
-		m_dbg->pushBackVertex(verts[id1].xyz());
-		m_dbg->pushBackVertex(verts[id2].xyz());
-
-		m_dbg->pushBackVertex(verts[id2].xyz());
-		m_dbg->pushBackVertex(verts[id0].xyz());
-	}
-	m_dbg->end();
-}
-
-void SceneDebugDrawer::draw(const PortalComponent& c) const
-{
-	const SceneNode& node = c.getSceneNode();
-	const PortalSectorBase& psnode = static_cast<const PortalSectorBase&>(node);
-
-	m_dbg->setColor(Vec3(0.0, 0.0, 0.5));
-
-	m_dbg->begin(PrimitiveTopology::LINES);
-	const auto& verts = psnode.getVertices();
-	ANKI_ASSERT((psnode.getVertexIndices().getSize() % 3) == 0);
-	for(U i = 0; i < psnode.getVertexIndices().getSize(); i += 3)
-	{
-		I id0 = psnode.getVertexIndices()[i];
-		I id1 = psnode.getVertexIndices()[i + 1];
-		I id2 = psnode.getVertexIndices()[i + 2];
-
-		m_dbg->pushBackVertex(verts[id0].xyz());
-		m_dbg->pushBackVertex(verts[id1].xyz());
-
-		m_dbg->pushBackVertex(verts[id1].xyz());
-		m_dbg->pushBackVertex(verts[id2].xyz());
-
-		m_dbg->pushBackVertex(verts[id2].xyz());
-		m_dbg->pushBackVertex(verts[id0].xyz());
-	}
-	m_dbg->end();
-}
-
-void SceneDebugDrawer::draw(const ReflectionProxyComponent& proxy) const
-{
-	m_dbg->setModelMatrix(Mat4::getIdentity());
-	m_dbg->begin(PrimitiveTopology::LINES);
-	for(const auto& face : proxy.getFaces())
-	{
-		for(U i = 0; i < 3; ++i)
-		{
-			m_dbg->setColor(Vec3(0.4, 0.4, 1.0));
-			m_dbg->pushBackVertex(face.m_vertices[i].xyz());
-			m_dbg->setColor(Vec3(1.0, 0.4, 0.4));
-			m_dbg->pushBackVertex(face.m_vertices[i + 1].xyz());
-		}
-	}
-	m_dbg->end();
-}
-
-void SceneDebugDrawer::draw(const DecalComponent& decalc) const
-{
-	const MoveComponent& movec = decalc.getSceneNode().getComponent<MoveComponent>();
-
-	m_dbg->setColor(Vec3(0.0, 1.0, 0.0));
-
-	const Vec3& size = decalc.getVolumeSize();
-	Vec3 halfSize = size / 2.0;
-	Obb box(Vec4(0.0, 0.0, -halfSize.z(), 0.0), Mat3x4::getIdentity(), Vec4(halfSize, 0.0));
-	box.transform(movec.getWorldTransform());
-	CollisionDebugDrawer cd(m_dbg);
-	box.accept(cd);
+	// TODO
 }
 
 } // end namespace anki

+ 4 - 10
src/anki/renderer/DebugDrawer.h

@@ -5,11 +5,11 @@
 
 #pragma once
 
+#include <anki/renderer/Common.h>
 #include <anki/Math.h>
 #include <anki/Gr.h>
 #include <anki/collision/CollisionShape.h>
 #include <anki/physics/PhysicsDrawer.h>
-#include <anki/scene/Forward.h>
 #include <anki/resource/ShaderResource.h>
 #include <anki/util/Array.h>
 
@@ -163,17 +163,11 @@ public:
 	{
 	}
 
-	void draw(const FrustumComponent& fr) const;
+	void draw(const RenderableQueueElement& r) const;
 
-	void draw(const SpatialComponent& sp) const;
+	void draw(const PointLightQueueElement& light) const;
 
-	void draw(const PortalComponent& c) const;
-
-	void draw(const SectorComponent& c) const;
-
-	void draw(const ReflectionProxyComponent& proxy) const;
-
-	void draw(const DecalComponent& decalc) const;
+	void draw(const SpotLightQueueElement& light) const;
 
 private:
 	DebugDrawer* m_dbg;

+ 7 - 13
src/anki/renderer/Drawer.cpp

@@ -4,12 +4,9 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/Drawer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/resource/ShaderResource.h>
-#include <anki/scene/FrustumComponent.h>
 #include <anki/resource/Material.h>
-#include <anki/scene/RenderComponent.h>
-#include <anki/scene/Visibility.h>
-#include <anki/scene/SceneGraph.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/core/Trace.h>
@@ -24,7 +21,7 @@ class DrawContext
 public:
 	RenderQueueDrawContext m_queueCtx;
 
-	const VisibleNode* m_visibleNode = nullptr;
+	const RenderableQueueElement* m_renderableElement = nullptr;
 
 	Array<RenderableQueueElement, MAX_INSTANCES> m_cachedRenderElements;
 	Array<U8, MAX_INSTANCES> m_cachedRenderElementLods;
@@ -46,8 +43,8 @@ void RenderableDrawer::drawRange(Pass pass,
 	const Mat4& viewMat,
 	const Mat4& viewProjMat,
 	CommandBufferPtr cmdb,
-	const VisibleNode* begin,
-	const VisibleNode* end)
+	const RenderableQueueElement* begin,
+	const RenderableQueueElement* end)
 {
 	ANKI_ASSERT(begin && end && begin < end);
 
@@ -62,7 +59,7 @@ void RenderableDrawer::drawRange(Pass pass,
 
 	for(; begin != end; ++begin)
 	{
-		ctx.m_visibleNode = begin;
+		ctx.m_renderableElement = begin;
 
 		drawSingle(ctx);
 	}
@@ -89,17 +86,14 @@ void RenderableDrawer::flushDrawcall(DrawContext& ctx)
 
 void RenderableDrawer::drawSingle(DrawContext& ctx)
 {
-	const RenderComponent& rc = ctx.m_visibleNode->m_node->getComponent<RenderComponent>();
-
 	if(ctx.m_cachedRenderElementCount == MAX_INSTANCES)
 	{
 		flushDrawcall(ctx);
 	}
 
-	RenderableQueueElement rqel;
-	rc.setupRenderableQueueElement(rqel);
+	const RenderableQueueElement& rqel = *ctx.m_renderableElement;
 
-	const F32 flod = min<F32>(m_r->calculateLod(ctx.m_visibleNode->m_frustumDistance), MAX_LOD_COUNT - 1);
+	const F32 flod = min<F32>(m_r->calculateLod(rqel.m_distanceFromCamera), MAX_LOD_COUNT - 1);
 	const U8 lod = U8(flod);
 
 	const Bool shouldFlush = ctx.m_cachedRenderElementCount > 0

+ 3 - 3
src/anki/renderer/Drawer.h

@@ -5,8 +5,8 @@
 
 #pragma once
 
+#include <anki/renderer/Common.h>
 #include <anki/resource/RenderingKey.h>
-#include <anki/scene/Forward.h>
 #include <anki/Gr.h>
 
 namespace anki
@@ -36,8 +36,8 @@ public:
 		const Mat4& viewMat,
 		const Mat4& viewProjMat,
 		CommandBufferPtr cmdb,
-		const VisibleNode* begin,
-		const VisibleNode* end);
+		const RenderableQueueElement* begin,
+		const RenderableQueueElement* end);
 
 private:
 	Renderer* m_r;

+ 0 - 2
src/anki/renderer/FinalComposite.cpp

@@ -16,8 +16,6 @@
 #include <anki/renderer/DownscaleBlur.h>
 #include <anki/util/Logger.h>
 #include <anki/misc/ConfigSet.h>
-#include <anki/scene/SceneNode.h>
-#include <anki/scene/FrustumComponent.h>
 
 namespace anki
 {

+ 9 - 9
src/anki/renderer/ForwardShading.cpp

@@ -5,13 +5,12 @@
 
 #include <anki/renderer/ForwardShading.h>
 #include <anki/renderer/Renderer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/DepthDownscale.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/FrustumComponent.h>
 
 namespace anki
 {
@@ -88,7 +87,7 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb
 	cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
 
 	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
-	computeLinearizeDepthOptimal(ctx.m_near, ctx.m_far, unis->x(), unis->y());
+	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, unis->x(), unis->y());
 
 	cmdb->informTextureSurfaceCurrentUsage(
 		m_r->getDepthDownscale().m_qd.m_depthRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
@@ -110,7 +109,7 @@ void ForwardShading::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb
 
 void ForwardShading::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount) const
 {
-	U problemSize = ctx.m_visResults->getCount(VisibilityGroupType::RENDERABLES_FS);
+	const U problemSize = ctx.m_renderQueue->m_forwardShadingRenderables.getSize();
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
 
@@ -152,11 +151,11 @@ void ForwardShading::buildCommandBuffers(RenderingContext& ctx, U threadId, U th
 
 	// Start drawing
 	m_r->getSceneDrawer().drawRange(Pass::GB_FS,
-		ctx.m_viewMat,
-		ctx.m_viewProjMat,
+		ctx.m_renderQueue->m_viewMatrix,
+		ctx.m_viewProjMatJitter,
 		cmdb,
-		ctx.m_visResults->getBegin(VisibilityGroupType::RENDERABLES_FS) + start,
-		ctx.m_visResults->getBegin(VisibilityGroupType::RENDERABLES_FS) + end);
+		ctx.m_renderQueue->m_forwardShadingRenderables.getBegin() + start,
+		ctx.m_renderQueue->m_forwardShadingRenderables.getBegin() + end);
 }
 
 void ForwardShading::setPreRunBarriers(RenderingContext& ctx)
@@ -236,7 +235,8 @@ void ForwardShadingUpscale::run(RenderingContext& ctx)
 	CommandBufferPtr cmdb = ctx.m_commandBuffer;
 
 	Vec4* linearDepth = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
-	computeLinearizeDepthOptimal(ctx.m_near, ctx.m_far, linearDepth->x(), linearDepth->y());
+	computeLinearizeDepthOptimal(
+		ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, linearDepth->x(), linearDepth->y());
 
 	cmdb->bindTexture(0, 0, m_r->getGBuffer().m_depthRt);
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_hd.m_depthRt, m_r->getNearestSampler());

+ 5 - 8
src/anki/renderer/GBuffer.cpp

@@ -5,10 +5,9 @@
 
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/Renderer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/util/Logger.h>
 #include <anki/util/ThreadPool.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/FrustumComponent.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/core/Trace.h>
 
@@ -105,9 +104,7 @@ void GBuffer::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCou
 	ANKI_TRACE_SCOPED_EVENT(RENDER_MS);
 
 	// Get some stuff
-	const VisibilityTestResults& vis = *ctx.m_visResults;
-
-	U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_MS);
+	const U problemSize = ctx.m_renderQueue->m_renderables.getSize();
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
 
@@ -136,11 +133,11 @@ void GBuffer::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCou
 
 		// Start drawing
 		m_r->getSceneDrawer().drawRange(Pass::GB_FS,
-			ctx.m_viewMat,
+			ctx.m_renderQueue->m_viewMatrix,
 			ctx.m_viewProjMatJitter,
 			cmdb,
-			vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
-			vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end);
+			ctx.m_renderQueue->m_renderables.getBegin() + start,
+			ctx.m_renderQueue->m_renderables.getBegin() + end);
 	}
 }
 

+ 64 - 96
src/anki/renderer/Indirect.cpp

@@ -7,13 +7,8 @@
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/FinalComposite.h>
 #include <anki/renderer/GBuffer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/core/Config.h>
-#include <anki/scene/SceneNode.h>
-#include <anki/scene/Visibility.h>
-#include <anki/scene/FrustumComponent.h>
-#include <anki/scene/MoveComponent.h>
-#include <anki/scene/LightComponent.h>
-#include <anki/scene/ReflectionProbeComponent.h>
 #include <anki/core/Trace.h>
 #include <anki/resource/MeshLoader.h>
 
@@ -310,10 +305,9 @@ Error Indirect::initIrradiance()
 	return ErrorCode::NONE;
 }
 
-void Indirect::runMs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx)
+void Indirect::runMs(RenderingContext& rctx, const RenderQueue& rqueue, U layer, U faceIdx)
 {
 	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
-	VisibilityTestResults& vis = frc.getVisibilityTestResults();
 
 	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
 
@@ -342,11 +336,11 @@ void Indirect::runMs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 
 	/// Draw
 	m_r->getSceneDrawer().drawRange(Pass::GB_FS,
-		frc.getViewMatrix(),
-		frc.getViewProjectionMatrix(),
+		rqueue.m_viewMatrix,
+		rqueue.m_viewProjectionMatrix,
 		cmdb,
-		vis.getBegin(VisibilityGroupType::RENDERABLES_MS),
-		vis.getEnd(VisibilityGroupType::RENDERABLES_MS));
+		rqueue.m_renderables.getBegin(),
+		rqueue.m_renderables.getEnd());
 
 	// End and set barriers
 	cmdb->endRenderPass();
@@ -365,10 +359,9 @@ void Indirect::runMs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 
-void Indirect::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx)
+void Indirect::runIs(RenderingContext& rctx, const RenderQueue& rqueue, U layer, U faceIdx)
 {
 	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
-	VisibilityTestResults& vis = frc.getVisibilityTestResults();
 	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
 
 	// Set barriers
@@ -393,64 +386,57 @@ void Indirect::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 	cmdb->setCullMode(FaceSelectionBit::FRONT);
 
 	// Process all lights
-	const Mat4& vpMat = frc.getViewProjectionMatrix();
-	const Mat4& vMat = frc.getViewMatrix();
+	const Mat4& vpMat = rqueue.m_viewProjectionMatrix;
+	const Mat4& vMat = rqueue.m_viewMatrix;
 
 	cmdb->bindShaderProgram(m_is.m_plightGrProg);
 	cmdb->bindVertexBuffer(0, m_is.m_plightPositions, 0, sizeof(F32) * 3);
 	cmdb->bindIndexBuffer(m_is.m_plightIndices, 0, IndexType::U16);
 
-	const VisibleNode* it = vis.getBegin(VisibilityGroupType::LIGHTS_POINT);
-	const VisibleNode* end = vis.getEnd(VisibilityGroupType::LIGHTS_POINT);
-	while(it != end)
+	const PointLightQueueElement* plightEl = rqueue.m_pointLights.getBegin();
+	const PointLightQueueElement* end = rqueue.m_pointLights.getEnd();
+	while(plightEl != end)
 	{
-		const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
-
 		// Update uniforms
 		IrVertex* vert = allocateAndBindUniforms<IrVertex*>(sizeof(IrVertex), cmdb, 0, 0);
 
-		Mat4 modelM(lightc.getWorldTransform().getOrigin().xyz1(),
-			lightc.getWorldTransform().getRotation().getRotationPart(),
-			lightc.getRadius());
+		Mat4 modelM(plightEl->m_worldPosition.xyz1(), Mat3::getIdentity(), plightEl->m_radius);
 
 		vert->m_mvp = vpMat * modelM;
 
 		IrPointLight* light = allocateAndBindUniforms<IrPointLight*>(sizeof(IrPointLight), cmdb, 0, 1);
 
-		Vec4 pos = vMat * lightc.getWorldTransform().getOrigin().xyz1();
+		Vec4 pos = vMat * plightEl->m_worldPosition.xyz1();
 
-		light->m_projectionParams = frc.getProjectionMatrix().extractPerspectiveUnprojectionParams();
-		light->m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getRadius() * lightc.getRadius()));
-		light->m_diffuseColorPad1 = lightc.getDiffuseColor();
-		light->m_specularColorPad1 = lightc.getSpecularColor();
+		light->m_projectionParams = rqueue.m_projectionMatrix.extractPerspectiveUnprojectionParams();
+		light->m_posRadius = Vec4(pos.xyz(), 1.0 / (plightEl->m_radius * plightEl->m_radius));
+		light->m_diffuseColorPad1 = plightEl->m_diffuseColor.xyz0();
+		light->m_specularColorPad1 = plightEl->m_specularColor.xyz0();
 
 		// Draw
 		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_is.m_plightIdxCount);
 
-		++it;
+		++plightEl;
 	}
 
 	cmdb->bindShaderProgram(m_is.m_slightGrProg);
 	cmdb->bindVertexBuffer(0, m_is.m_slightPositions, 0, sizeof(F32) * 3);
 	cmdb->bindIndexBuffer(m_is.m_slightIndices, 0, IndexType::U16);
 
-	it = vis.getBegin(VisibilityGroupType::LIGHTS_SPOT);
-	end = vis.getEnd(VisibilityGroupType::LIGHTS_SPOT);
-	while(it != end)
+	const SpotLightQueueElement* splightEl = rqueue.m_spotLights.getBegin();
+	while(splightEl != rqueue.m_spotLights.getEnd())
 	{
-		const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
-
 		// Compute the model matrix
 		//
-		Mat4 modelM(lightc.getWorldTransform().getOrigin().xyz1(),
-			lightc.getWorldTransform().getRotation().getRotationPart(),
-			1.0);
+		Mat4 modelM(splightEl->m_worldTransform.getTranslationPart().xyz1(),
+			splightEl->m_worldTransform.getRotationPart(),
+			1.0f);
 
 		// Calc the scale of the cone
 		Mat4 scaleM(Mat4::getIdentity());
-		scaleM(0, 0) = tan(lightc.getOuterAngle() / 2.0) * lightc.getDistance();
+		scaleM(0, 0) = tan(splightEl->m_outerAngle / 2.0f) * splightEl->m_distance;
 		scaleM(1, 1) = scaleM(0, 0);
-		scaleM(2, 2) = lightc.getDistance();
+		scaleM(2, 2) = splightEl->m_distance;
 
 		modelM = modelM * scaleM;
 
@@ -461,23 +447,23 @@ void Indirect::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U f
 		// Update fragment uniforms
 		IrSpotLight* light = allocateAndBindUniforms<IrSpotLight*>(sizeof(IrSpotLight), cmdb, 0, 1);
 
-		light->m_projectionParams = frc.getProjectionMatrix().extractPerspectiveUnprojectionParams();
+		light->m_projectionParams = rqueue.m_projectionMatrix.extractPerspectiveUnprojectionParams();
 
-		Vec4 pos = vMat * lightc.getWorldTransform().getOrigin().xyz1();
-		light->m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getDistance() * lightc.getDistance()));
+		Vec4 pos = vMat * splightEl->m_worldTransform.getTranslationPart().xyz1();
+		light->m_posRadius = Vec4(pos.xyz(), 1.0 / (splightEl->m_distance * splightEl->m_distance));
 
-		light->m_diffuseColorOuterCos = Vec4(lightc.getDiffuseColor().xyz(), lightc.getOuterAngleCos());
+		light->m_diffuseColorOuterCos = Vec4(splightEl->m_diffuseColor, cos(splightEl->m_outerAngle / 2.0f));
 
-		light->m_specularColorInnerCos = Vec4(lightc.getSpecularColor().xyz(), lightc.getInnerAngleCos());
+		light->m_specularColorInnerCos = Vec4(splightEl->m_specularColor, cos(splightEl->m_innerAngle / 2.0f));
 
-		Vec3 lightDir = -lightc.getWorldTransform().getRotation().getZAxis();
+		Vec3 lightDir = -splightEl->m_worldTransform.getZAxis().xyz();
 		lightDir = vMat.getRotationPart() * lightDir;
 		light->m_lightDirPad1 = lightDir.xyz0();
 
 		// Draw
 		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_is.m_slightIdxCount);
 
-		++it;
+		++splightEl;
 	}
 
 	// Generate mips
@@ -545,28 +531,44 @@ void Indirect::computeIrradiance(RenderingContext& rctx, U layer, U faceIdx)
 void Indirect::run(RenderingContext& rctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RENDER_IR);
-	const VisibilityTestResults& visRez = *rctx.m_visResults;
 
-	if(visRez.getCount(VisibilityGroupType::REFLECTION_PROBES) > m_cubemapArrSize)
+	if(rctx.m_renderQueue->m_reflectionProbes.getSize() > m_cubemapArrSize)
 	{
 		ANKI_R_LOGW("Increase the ir.cubemapTextureArraySize");
 	}
 
 	// Render some of the probes
-	const VisibleNode* it = visRez.getBegin(VisibilityGroupType::REFLECTION_PROBES);
-	const VisibleNode* end = visRez.getEnd(VisibilityGroupType::REFLECTION_PROBES);
+	ReflectionProbeQueueElement* it = rctx.m_renderQueue->m_reflectionProbes.getBegin();
+	const ReflectionProbeQueueElement* end = rctx.m_renderQueue->m_reflectionProbes.getEnd();
 
 	U probesRendered = 0;
-	U probeIdx = 0;
 	while(it != end)
 	{
 		// Write and render probe
-		tryRender(rctx, *it->m_node, probesRendered);
 
+		Bool render = false;
+		U entry;
+		findCacheEntry(it->m_uuid, entry, render);
+		it->m_textureArrayIndex = entry;
+
+		if(it->m_renderQueues[0] && probesRendered < MAX_PROBE_RENDERS_PER_FRAME)
+		{
+			++probesRendered;
+			renderReflection(*it, rctx, entry);
+
+			// Rendered, no need to render it again next frame
+			it->m_feedbackCallback(false, it->m_userData);
+		}
+
+		// If you need to render it mark it for the next frame
+		if(render)
+		{
+			it->m_feedbackCallback(true, it->m_userData);
+		}
+
+		// Advance
 		++it;
-		++probeIdx;
 	}
-	ANKI_ASSERT(probeIdx == visRez.getCount(VisibilityGroupType::REFLECTION_PROBES));
 
 	// Inform on tex usage
 	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
@@ -574,50 +576,18 @@ void Indirect::run(RenderingContext& rctx)
 	cmdb->informTextureCurrentUsage(m_is.m_lightRt, TextureUsageBit::SAMPLED_FRAGMENT);
 }
 
-void Indirect::tryRender(RenderingContext& ctx, SceneNode& node, U& probesRendered)
-{
-	ReflectionProbeComponent& reflc = node.getComponent<ReflectionProbeComponent>();
-
-	Bool render = false;
-	U entry;
-	findCacheEntry(node, entry, render);
-
-	// Write shader var
-	reflc.setTextureArrayIndex(entry);
-
-	if(reflc.getMarkedForRendering() && probesRendered < MAX_PROBE_RENDERS_PER_FRAME)
-	{
-		++probesRendered;
-		reflc.setMarkedForRendering(false);
-		renderReflection(ctx, node, entry);
-	}
-
-	// If you need to render it mark it for the next frame
-	if(render)
-	{
-		reflc.setMarkedForRendering(true);
-	}
-}
-
-void Indirect::renderReflection(RenderingContext& ctx, SceneNode& node, U cubemapIdx)
+void Indirect::renderReflection(const ReflectionProbeQueueElement& probeEl, RenderingContext& ctx, U cubemapIdx)
 {
 	ANKI_TRACE_INC_COUNTER(RENDERER_REFLECTIONS, 1);
 
-	// Gather the frustum components
-	Array<FrustumComponent*, 6> frustumComponents;
-	U count = 0;
-	Error err = node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
-		frustumComponents[count++] = &frc;
-		return ErrorCode::NONE;
-	});
-	(void)err;
-	ANKI_ASSERT(count == 6);
-
 	// Render cubemap
 	for(U i = 0; i < 6; ++i)
 	{
-		runMs(ctx, *frustumComponents[i], cubemapIdx, i);
-		runIs(ctx, *frustumComponents[i], cubemapIdx, i);
+		const RenderQueue* rqueue = probeEl.m_renderQueues[i];
+		ANKI_ASSERT(rqueue);
+
+		runMs(ctx, *rqueue, cubemapIdx, i);
+		runIs(ctx, *rqueue, cubemapIdx, i);
 	}
 
 	for(U i = 0; i < 6; ++i)
@@ -626,10 +596,8 @@ void Indirect::renderReflection(RenderingContext& ctx, SceneNode& node, U cubema
 	}
 }
 
-void Indirect::findCacheEntry(SceneNode& node, U& entry, Bool& render)
+void Indirect::findCacheEntry(U64 uuid, U& entry, Bool& render)
 {
-	U64 uuid = node.getUuid();
-
 	// Try find a candidate cache entry
 	CacheEntry* candidate = nullptr;
 	auto it = m_uuidToCacheEntry.find(uuid);

+ 4 - 8
src/anki/renderer/Indirect.h

@@ -17,7 +17,6 @@ namespace anki
 struct IrShaderReflectionProbe;
 class IrRunContext;
 class IrTaskContext;
-class ReflectionProbeComponent;
 
 /// @addtogroup renderer
 /// @{
@@ -141,17 +140,14 @@ private:
 	void initFaceInfo(U cacheEntryIdx, U faceIdx);
 	ANKI_USE_RESULT Error loadMesh(CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount);
 
-	// Rendering
-	void tryRender(RenderingContext& ctx, SceneNode& node, U& probesRendered);
-
-	void runMs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx);
-	void runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx);
+	void runMs(RenderingContext& rctx, const RenderQueue& rqueue, U layer, U faceIdx);
+	void runIs(RenderingContext& rctx, const RenderQueue& rqueue, U layer, U faceIdx);
 	void computeIrradiance(RenderingContext& rctx, U layer, U faceIdx);
 
-	void renderReflection(RenderingContext& ctx, SceneNode& node, U cubemapIdx);
+	void renderReflection(const ReflectionProbeQueueElement& probeEl, RenderingContext& ctx, U cubemapIdx);
 
 	/// Find a cache entry to store the reflection.
-	void findCacheEntry(SceneNode& node, U& entry, Bool& render);
+	void findCacheEntry(U64 nodeUuid, U& entry, Bool& render);
 };
 /// @}
 

+ 16 - 23
src/anki/renderer/LensFlare.cpp

@@ -6,11 +6,8 @@
 #include <anki/renderer/LensFlare.h>
 #include <anki/renderer/Bloom.h>
 #include <anki/renderer/GBuffer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/Renderer.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/MoveComponent.h>
-#include <anki/scene/LensFlareComponent.h>
-#include <anki/scene/FrustumComponent.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/Functions.h>
 
@@ -107,12 +104,12 @@ Error LensFlare::initOcclusion(const ConfigSet& config)
 
 void LensFlare::resetOcclusionQueries(RenderingContext& ctx, CommandBufferPtr cmdb)
 {
-	if(ctx.m_visResults->getCount(VisibilityGroupType::FLARES) > m_maxFlares)
+	if(ctx.m_renderQueue->m_lensFlares.getSize() > m_maxFlares)
 	{
 		ANKI_R_LOGW("Visible flares exceed the limit. Increase lf.maxFlares");
 	}
 
-	const U count = min<U>(ctx.m_visResults->getCount(VisibilityGroupType::FLARES), m_maxFlares);
+	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
 	for(U i = 0; i < count; ++i)
 	{
 		if(!m_queries[i])
@@ -126,14 +123,14 @@ void LensFlare::resetOcclusionQueries(RenderingContext& ctx, CommandBufferPtr cm
 
 void LensFlare::runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb)
 {
-	const U count = min<U>(ctx.m_visResults->getCount(VisibilityGroupType::FLARES), m_maxFlares);
+	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
 	Vec3* positions = nullptr;
 	const Vec3* initialPositions;
 	if(count)
 	{
 		// Setup MVP UBO
 		Mat4* mvp = allocateAndBindUniforms<Mat4*>(sizeof(Mat4), cmdb, 0, 0);
-		*mvp = ctx.m_viewProjMat;
+		*mvp = ctx.m_renderQueue->m_viewProjectionMatrix;
 
 		// Alloc dyn mem
 		StagingGpuMemoryToken vertToken;
@@ -154,11 +151,7 @@ void LensFlare::runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 	for(U i = 0; i < count; ++i)
 	{
-		// Iterate lens flare
-		auto it = ctx.m_visResults->getBegin(VisibilityGroupType::FLARES) + i;
-		const LensFlareComponent& lf = (it->m_node)->getComponent<LensFlareComponent>();
-
-		*positions = lf.getWorldPosition().xyz();
+		*positions = ctx.m_renderQueue->m_lensFlares[i].m_worldPosition;
 
 		// Draw and query
 		cmdb->beginOcclusionQuery(m_queries[i]);
@@ -180,7 +173,7 @@ void LensFlare::runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 void LensFlare::updateIndirectInfo(RenderingContext& ctx, CommandBufferPtr cmdb)
 {
-	U count = min<U>(ctx.m_visResults->getCount(VisibilityGroupType::FLARES), m_maxFlares);
+	U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
 	if(count == 0)
 	{
 		return;
@@ -215,7 +208,7 @@ void LensFlare::updateIndirectInfo(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 void LensFlare::run(RenderingContext& ctx, CommandBufferPtr cmdb)
 {
-	const U count = min<U>(ctx.m_visResults->getCount(VisibilityGroupType::FLARES), m_maxFlares);
+	const U count = min<U>(ctx.m_renderQueue->m_lensFlares.getSize(), m_maxFlares);
 	if(count == 0)
 	{
 		return;
@@ -230,12 +223,11 @@ void LensFlare::run(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 	for(U i = 0; i < count; ++i)
 	{
-		auto it = ctx.m_visResults->getBegin(VisibilityGroupType::FLARES) + i;
-		const LensFlareComponent& lf = (it->m_node)->getComponent<LensFlareComponent>();
+		const LensFlareQueueElement& flareEl = ctx.m_renderQueue->m_lensFlares[i];
 
 		// Compute position
-		Vec4 lfPos = Vec4(lf.getWorldPosition().xyz(), 1.0);
-		Vec4 posClip = ctx.m_viewProjMat * lfPos;
+		Vec4 lfPos = Vec4(flareEl.m_worldPosition, 1.0);
+		Vec4 posClip = ctx.m_renderQueue->m_viewProjectionMatrix * lfPos;
 
 		/*if(posClip.x() > posClip.w() || posClip.x() < -posClip.w() || posClip.y() > posClip.w()
 			|| posClip.y() < -posClip.w())
@@ -256,15 +248,16 @@ void LensFlare::run(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 		// First flare
 		sprites[c].m_pos = posNdc;
-		sprites[c].m_scale = lf.getFirstFlareSize() * Vec2(1.0, m_r->getAspectRatio());
+		sprites[c].m_scale = flareEl.m_firstFlareSize * Vec2(1.0, m_r->getAspectRatio());
 		sprites[c].m_depth = 0.0;
-		F32 alpha = lf.getColorMultiplier().w() * (1.0 - pow(absolute(posNdc.x()), 6.0))
+		F32 alpha = flareEl.m_colorMultiplier.w() * (1.0 - pow(absolute(posNdc.x()), 6.0))
 			* (1.0 - pow(absolute(posNdc.y()), 6.0)); // Fade the flare on the edges
-		sprites[c].m_color = Vec4(lf.getColorMultiplier().xyz(), alpha);
+		sprites[c].m_color = Vec4(flareEl.m_colorMultiplier.xyz(), alpha);
 		++c;
 
 		// Render
-		cmdb->bindTexture(0, 0, lf.getTexture());
+		ANKI_ASSERT(flareEl.m_texture);
+		cmdb->bindTexture(0, 0, TexturePtr(flareEl.m_texture));
 
 		cmdb->drawArraysIndirect(
 			PrimitiveTopology::TRIANGLE_STRIP, 1, i * sizeof(DrawArraysIndirectInfo), m_indirectBuff);

+ 95 - 121
src/anki/renderer/LightBin.cpp

@@ -4,14 +4,11 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/LightBin.h>
-#include <anki/scene/FrustumComponent.h>
-#include <anki/scene/Visibility.h>
-#include <anki/scene/MoveComponent.h>
-#include <anki/scene/LightComponent.h>
-#include <anki/scene/DecalComponent.h>
-#include <anki/scene/ReflectionProbeComponent.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/core/Trace.h>
 #include <anki/util/ThreadPool.h>
+#include <anki/collision/Sphere.h>
+#include <anki/collision/Frustum.h>
 
 namespace anki
 {
@@ -313,10 +310,10 @@ public:
 	Atomic<U32> m_lightIdsCount = {0};
 
 	// Misc
-	WeakArray<const VisibleNode> m_vPointLights;
-	WeakArray<const VisibleNode> m_vSpotLights;
-	WeakArray<const VisibleNode> m_vProbes;
-	WeakArray<const VisibleNode> m_vDecals;
+	WeakArray<const PointLightQueueElement> m_vPointLights;
+	WeakArray<const SpotLightQueueElement> m_vSpotLights;
+	WeakArray<const ReflectionProbeQueueElement> m_vProbes;
+	WeakArray<const DecalQueueElement> m_vDecals;
 
 	Atomic<U32> m_count = {0};
 	Atomic<U32> m_count2 = {0};
@@ -365,7 +362,7 @@ Error LightBin::bin(const Mat4& viewMat,
 	const Mat4& projMat,
 	const Mat4& viewProjMat,
 	const Mat4& camTrf,
-	const VisibilityTestResults& vi,
+	const RenderQueue& rqueue,
 	StackAllocator<U8> frameAlloc,
 	U maxLightIndices,
 	Bool shadowsEnabled,
@@ -390,10 +387,10 @@ Error LightBin::bin(const Mat4& viewMat,
 	//
 	// Quickly get the lights
 	//
-	U visiblePointLightsCount = vi.getCount(VisibilityGroupType::LIGHTS_POINT);
-	U visibleSpotLightsCount = vi.getCount(VisibilityGroupType::LIGHTS_SPOT);
-	U visibleProbeCount = vi.getCount(VisibilityGroupType::REFLECTION_PROBES);
-	U visibleDecalCount = vi.getCount(VisibilityGroupType::DECALS);
+	const U visiblePointLightsCount = rqueue.m_pointLights.getSize();
+	const U visibleSpotLightsCount = rqueue.m_spotLights.getSize();
+	const U visibleProbeCount = rqueue.m_reflectionProbes.getSize();
+	const U visibleDecalCount = rqueue.m_decals.getSize();
 
 	ANKI_TRACE_INC_COUNTER(RENDERER_LIGHTS, visiblePointLightsCount + visibleSpotLightsCount);
 
@@ -417,7 +414,7 @@ Error LightBin::bin(const Mat4& viewMat,
 		ctx.m_pointLights = WeakArray<ShaderPointLight>(data, visiblePointLightsCount);
 
 		ctx.m_vPointLights =
-			WeakArray<const VisibleNode>(vi.getBegin(VisibilityGroupType::LIGHTS_POINT), visiblePointLightsCount);
+			WeakArray<const PointLightQueueElement>(rqueue.m_pointLights.getBegin(), visiblePointLightsCount);
 	}
 	else
 	{
@@ -432,7 +429,7 @@ Error LightBin::bin(const Mat4& viewMat,
 		ctx.m_spotLights = WeakArray<ShaderSpotLight>(data, visibleSpotLightsCount);
 
 		ctx.m_vSpotLights =
-			WeakArray<const VisibleNode>(vi.getBegin(VisibilityGroupType::LIGHTS_SPOT), visibleSpotLightsCount);
+			WeakArray<const SpotLightQueueElement>(rqueue.m_spotLights.getBegin(), visibleSpotLightsCount);
 	}
 	else
 	{
@@ -449,7 +446,7 @@ Error LightBin::bin(const Mat4& viewMat,
 			ctx.m_probes = WeakArray<ShaderProbe>(data, visibleProbeCount);
 
 			ctx.m_vProbes =
-				WeakArray<const VisibleNode>(vi.getBegin(VisibilityGroupType::REFLECTION_PROBES), visibleProbeCount);
+				WeakArray<const ReflectionProbeQueueElement>(rqueue.m_reflectionProbes.getBegin(), visibleProbeCount);
 		}
 		else
 		{
@@ -464,7 +461,7 @@ Error LightBin::bin(const Mat4& viewMat,
 
 		ctx.m_decals = WeakArray<ShaderDecal>(data, visibleDecalCount);
 
-		ctx.m_vDecals = WeakArray<const VisibleNode>(vi.getBegin(VisibilityGroupType::DECALS), visibleDecalCount);
+		ctx.m_vDecals = WeakArray<const DecalQueueElement>(rqueue.m_decals.getBegin(), visibleDecalCount);
 	}
 	else
 	{
@@ -548,38 +545,22 @@ void LightBin::binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ct
 			if(j >= lightCount + ctx.m_vDecals.getSize())
 			{
 				U i = j - (lightCount + ctx.m_vDecals.getSize());
-				SceneNode& snode = *ctx.m_vProbes[i].m_node;
-				writeAndBinProbe(snode, ctx, testResult);
+				writeAndBinProbe(ctx.m_vProbes[i], ctx, testResult);
 			}
 			else if(j >= ctx.m_vPointLights.getSize() + ctx.m_vDecals.getSize())
 			{
 				U i = j - (ctx.m_vPointLights.getSize() + ctx.m_vDecals.getSize());
-
-				SceneNode& snode = *ctx.m_vSpotLights[i].m_node;
-				LightComponent& light = snode.getComponent<LightComponent>();
-				SpatialComponent& sp = snode.getComponent<SpatialComponent>();
-				const FrustumComponent* frc = snode.tryGetComponent<FrustumComponent>();
-
-				I pos = writeSpotLight(light, frc, ctx);
-				binLight(sp, light, pos, 1, ctx, testResult);
+				writeAndBinSpotLight(ctx.m_vSpotLights[i], ctx, testResult);
 			}
 			else if(j >= ctx.m_vDecals.getSize())
 			{
 				U i = j - ctx.m_vDecals.getSize();
-
-				SceneNode& snode = *ctx.m_vPointLights[i].m_node;
-				LightComponent& light = snode.getComponent<LightComponent>();
-				SpatialComponent& sp = snode.getComponent<SpatialComponent>();
-
-				I pos = writePointLight(light, ctx);
-				binLight(sp, light, pos, 0, ctx, testResult);
+				writeAndBinPointLight(ctx.m_vPointLights[i], ctx, testResult);
 			}
 			else
 			{
 				U i = j;
-
-				SceneNode& snode = *ctx.m_vDecals[i].m_node;
-				writeAndBinDecal(snode, ctx, testResult);
+				writeAndBinDecal(ctx.m_vDecals[i], ctx, testResult);
 			}
 		}
 	}
@@ -675,90 +656,94 @@ void LightBin::binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ct
 	ANKI_TRACE_STOP_EVENT(RENDERER_LIGHT_BINNING);
 }
 
-I LightBin::writePointLight(const LightComponent& lightc, LightBinContext& ctx)
+void LightBin::writeAndBinPointLight(
+	const PointLightQueueElement& lightEl, LightBinContext& ctx, ClustererTestResult& testResult)
 {
 	// Get GPU light
-	I i = ctx.m_pointLightsCount.fetchAdd(1);
+	I idx = ctx.m_pointLightsCount.fetchAdd(1);
 
-	ShaderPointLight& slight = ctx.m_pointLights[i];
+	ShaderPointLight& slight = ctx.m_pointLights[idx];
 
-	Vec4 pos = ctx.m_viewMat * lightc.getWorldTransform().getOrigin().xyz1();
+	Vec4 pos = ctx.m_viewMat * lightEl.m_worldPosition.xyz1();
 
-	slight.m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getRadius() * lightc.getRadius()));
-	slight.m_diffuseColorShadowmapId = lightc.getDiffuseColor();
+	slight.m_posRadius = Vec4(pos.xyz(), 1.0f / (lightEl.m_radius * lightEl.m_radius));
+	slight.m_diffuseColorShadowmapId = lightEl.m_diffuseColor.xyz0();
 
-	if(!lightc.getShadowEnabled() || !ctx.m_shadowsEnabled)
+	if(lightEl.m_shadowRenderQueues[0] == nullptr || !ctx.m_shadowsEnabled)
 	{
 		slight.m_diffuseColorShadowmapId.w() = INVALID_TEXTURE_INDEX;
 	}
 	else
 	{
-		slight.m_diffuseColorShadowmapId.w() = lightc.getShadowMapIndex();
+		slight.m_diffuseColorShadowmapId.w() = lightEl.m_textureArrayIndex;
 	}
 
-	slight.m_specularColorRadius = Vec4(lightc.getSpecularColor().xyz(), lightc.getRadius());
+	slight.m_specularColorRadius = Vec4(lightEl.m_specularColor, lightEl.m_radius);
+
+	// Now bin it
+	Sphere sphere(lightEl.m_worldPosition.xyz0(), lightEl.m_radius);
+	Aabb box;
+	sphere.computeAabb(box);
+	m_clusterer.bin(sphere, box, testResult);
+
+	auto it = testResult.getClustersBegin();
+	auto end = testResult.getClustersEnd();
+	for(; it != end; ++it)
+	{
+		U x = (*it).x();
+		U y = (*it).y();
+		U z = (*it).z();
+
+		U i = m_clusterer.getClusterCountX() * (z * m_clusterer.getClusterCountY() + y) + x;
 
-	return i;
+		auto& cluster = ctx.m_tempClusters[i];
+
+		i = cluster.m_pointCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		cluster.m_pointIds[i].setIndex(idx);
+	}
 }
 
-I LightBin::writeSpotLight(const LightComponent& lightc, const FrustumComponent* lightFrc, LightBinContext& ctx)
+void LightBin::writeAndBinSpotLight(
+	const SpotLightQueueElement& lightEl, LightBinContext& ctx, ClustererTestResult& testResult)
 {
-	I i = ctx.m_spotLightsCount.fetchAdd(1);
+	I idx = ctx.m_spotLightsCount.fetchAdd(1);
 
-	ShaderSpotLight& light = ctx.m_spotLights[i];
+	ShaderSpotLight& light = ctx.m_spotLights[idx];
 	F32 shadowmapIndex = INVALID_TEXTURE_INDEX;
 
-	if(lightc.getShadowEnabled() && ctx.m_shadowsEnabled)
+	if(lightEl.m_shadowRenderQueue != nullptr && ctx.m_shadowsEnabled)
 	{
-		// Write matrix
-		static const Mat4 biasMat4(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0);
 		// bias * proj_l * view_l * world_c
-		light.m_texProjectionMat = biasMat4 * lightFrc->getViewProjectionMatrix() * ctx.m_camTrf;
+		light.m_texProjectionMat = lightEl.m_textureMatrix * ctx.m_camTrf;
 
-		shadowmapIndex = F32(lightc.getShadowMapIndex());
+		shadowmapIndex = F32(lightEl.m_textureArrayIndex);
 	}
 
 	// Pos & dist
-	Vec4 pos = ctx.m_viewMat * lightc.getWorldTransform().getOrigin().xyz1();
-	light.m_posRadius = Vec4(pos.xyz(), 1.0 / (lightc.getDistance() * lightc.getDistance()));
+	Vec4 pos = ctx.m_viewMat * lightEl.m_worldTransform.getTranslationPart().xyz1();
+	light.m_posRadius = Vec4(pos.xyz(), 1.0f / (lightEl.m_distance * lightEl.m_distance));
 
 	// Diff color and shadowmap ID now
-	light.m_diffuseColorShadowmapId = Vec4(lightc.getDiffuseColor().xyz(), shadowmapIndex);
+	light.m_diffuseColorShadowmapId = Vec4(lightEl.m_diffuseColor, shadowmapIndex);
 
 	// Spec color
-	light.m_specularColorRadius = Vec4(lightc.getSpecularColor().xyz(), lightc.getDistance());
+	light.m_specularColorRadius = Vec4(lightEl.m_specularColor, lightEl.m_distance);
 
 	// Light dir
-	Vec3 lightDir = -lightc.getWorldTransform().getRotation().getZAxis();
+	Vec3 lightDir = -lightEl.m_worldTransform.getRotationPart().getZAxis();
 	lightDir = ctx.m_viewMat.getRotationPart() * lightDir;
-	light.m_lightDir = Vec4(lightDir, 0.0);
+	light.m_lightDir = Vec4(lightDir, 0.0f);
 
 	// Angles
-	light.m_outerCosInnerCos = Vec4(lightc.getOuterAngleCos(), lightc.getInnerAngleCos(), 1.0, 1.0);
+	light.m_outerCosInnerCos = Vec4(cos(lightEl.m_outerAngle / 2.0f), cos(lightEl.m_innerAngle / 2.0f), 1.0f, 1.0f);
 
-	return i;
-}
+	// Bin lights
+	PerspectiveFrustum shape(lightEl.m_outerAngle, lightEl.m_outerAngle, 0.01f, lightEl.m_distance);
+	shape.transform(Transform(lightEl.m_worldTransform));
+	Aabb box;
+	shape.computeAabb(box);
+	m_clusterer.binPerspectiveFrustum(shape, box, testResult);
 
-void LightBin::binLight(const SpatialComponent& sp,
-	const LightComponent& lightc,
-	U pos,
-	U lightType,
-	LightBinContext& ctx,
-	ClustererTestResult& testResult) const
-{
-	if(lightc.getLightComponentType() == LightComponentType::SPOT)
-	{
-		const FrustumComponent& frc = lightc.getSceneNode().getComponent<FrustumComponent>();
-		ANKI_ASSERT(frc.getFrustum().getType() == FrustumType::PERSPECTIVE);
-		m_clusterer.binPerspectiveFrustum(
-			static_cast<const PerspectiveFrustum&>(frc.getFrustum()), sp.getAabb(), testResult);
-	}
-	else
-	{
-		m_clusterer.bin(sp.getSpatialCollisionShape(), sp.getAabb(), testResult);
-	}
-
-	// Bin to the correct tiles
 	auto it = testResult.getClustersBegin();
 	auto end = testResult.getClustersEnd();
 	for(; it != end; ++it)
@@ -771,38 +756,28 @@ void LightBin::binLight(const SpatialComponent& sp,
 
 		auto& cluster = ctx.m_tempClusters[i];
 
-		switch(lightType)
-		{
-		case 0:
-			i = cluster.m_pointCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
-			cluster.m_pointIds[i].setIndex(pos);
-			break;
-		case 1:
-			i = cluster.m_spotCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
-			cluster.m_spotIds[i].setIndex(pos);
-			break;
-		default:
-			ANKI_ASSERT(0);
-		}
+		i = cluster.m_spotCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
+		cluster.m_spotIds[i].setIndex(idx);
 	}
 }
 
-void LightBin::writeAndBinProbe(const SceneNode& node, LightBinContext& ctx, ClustererTestResult& testResult)
+void LightBin::writeAndBinProbe(
+	const ReflectionProbeQueueElement& probeEl, LightBinContext& ctx, ClustererTestResult& testResult)
 {
-	const ReflectionProbeComponent& reflc = node.getComponent<ReflectionProbeComponent>();
-	const SpatialComponent& sp = node.getComponent<SpatialComponent>();
-
 	// Write it
 	ShaderProbe probe;
-	probe.m_pos = reflc.getPosition().xyz();
-	probe.m_radiusSq = reflc.getRadius() * reflc.getRadius();
-	probe.m_cubemapIndex = reflc.getTextureArrayIndex();
+	probe.m_pos = probeEl.m_worldPosition;
+	probe.m_radiusSq = probeEl.m_radius * probeEl.m_radius;
+	probe.m_cubemapIndex = probeEl.m_textureArrayIndex;
 
 	U idx = ctx.m_probeCount.fetchAdd(1);
 	ctx.m_probes[idx] = probe;
 
 	// Bin it
-	m_clusterer.bin(sp.getSpatialCollisionShape(), sp.getAabb(), testResult);
+	Sphere sphere(probeEl.m_worldPosition.xyz0(), probeEl.m_radius);
+	Aabb box;
+	sphere.computeAabb(box);
+	m_clusterer.bin(sphere, box, testResult);
 
 	auto it = testResult.getClustersBegin();
 	auto end = testResult.getClustersEnd();
@@ -818,24 +793,19 @@ void LightBin::writeAndBinProbe(const SceneNode& node, LightBinContext& ctx, Clu
 
 		i = cluster.m_probeCount.fetchAdd(1) % MAX_PROBES_PER_CLUSTER;
 		cluster.m_probeIds[i].setIndex(idx);
-		cluster.m_probeIds[i].setProbeRadius(reflc.getRadius());
+		cluster.m_probeIds[i].setProbeRadius(probeEl.m_radius);
 	}
 }
 
-void LightBin::writeAndBinDecal(const SceneNode& node, LightBinContext& ctx, ClustererTestResult& testResult)
+void LightBin::writeAndBinDecal(const DecalQueueElement& decalEl, LightBinContext& ctx, ClustererTestResult& testResult)
 {
-	const DecalComponent& decalc = node.getComponent<DecalComponent>();
-	const SpatialComponent& sp = node.getComponent<SpatialComponent>();
-
 	I idx = ctx.m_decalCount.fetchAdd(1);
 	ShaderDecal& decal = ctx.m_decals[idx];
 
-	TexturePtr atlas;
-	Vec4 uv;
-	F32 blendFactor;
-	decalc.getDiffuseAtlasInfo(uv, atlas, blendFactor);
+	TexturePtr atlas(decalEl.m_diffuseAtlas);
+	Vec4 uv = decalEl.m_diffuseAtlasUv;
 	decal.m_diffUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
-	decal.m_blendFactors[0] = blendFactor;
+	decal.m_blendFactors[0] = decalEl.m_diffuseAtlasBlendFactor;
 
 	{
 		LockGuard<SpinLock> lock(ctx.m_diffDecalTexAtlasMtx);
@@ -847,9 +817,10 @@ void LightBin::writeAndBinDecal(const SceneNode& node, LightBinContext& ctx, Clu
 		ctx.m_diffDecalTexAtlas = atlas;
 	}
 
-	decalc.getNormalRoughnessAtlasInfo(uv, atlas, blendFactor);
+	atlas.reset(decalEl.m_normalRoughnessAtlas);
+	uv = decalEl.m_normalRoughnessAtlasUv;
 	decal.m_normRoughnessUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
-	decal.m_blendFactors[1] = blendFactor;
+	decal.m_blendFactors[1] = decalEl.m_normalRoughnessAtlasBlendFactor;
 
 	if(atlas)
 	{
@@ -863,10 +834,13 @@ void LightBin::writeAndBinDecal(const SceneNode& node, LightBinContext& ctx, Clu
 	}
 
 	// bias * proj_l * view_l * world_c
-	decal.m_texProjectionMat = decalc.getBiasProjectionViewMatrix() * ctx.m_camTrf;
+	decal.m_texProjectionMat = decalEl.m_textureMatrix * ctx.m_camTrf;
 
 	// Bin it
-	m_clusterer.bin(sp.getSpatialCollisionShape(), sp.getAabb(), testResult);
+	Obb obb(decalEl.m_obbCenter.xyz0(), Mat3x4(decalEl.m_obbRotation), decalEl.m_obbExtend.xyz0());
+	Aabb box;
+	obb.computeAabb(box);
+	m_clusterer.bin(obb, box, testResult);
 
 	auto it = testResult.getClustersBegin();
 	auto end = testResult.getClustersEnd();

+ 8 - 16
src/anki/renderer/LightBin.h

@@ -11,11 +11,7 @@ namespace anki
 {
 
 // Forward
-class MoveComponent;
 class LightBinContext;
-class SpatialComponent;
-class LightComponent;
-class VisibilityTestResults;
 
 /// @addtogroup renderer
 /// @{
@@ -39,7 +35,7 @@ public:
 		const Mat4& projMat,
 		const Mat4& viewProjMat,
 		const Mat4& camTrf,
-		const VisibilityTestResults& vi,
+		const RenderQueue& rqueue,
 		StackAllocator<U8> frameAlloc,
 		U maxLightIndices,
 		Bool shadowsEnabled,
@@ -67,20 +63,16 @@ private:
 
 	void binLights(U32 threadId, PtrSize threadsCount, LightBinContext& ctx);
 
-	I writePointLight(const LightComponent& light, LightBinContext& ctx);
+	void writeAndBinPointLight(
+		const PointLightQueueElement& lightEl, LightBinContext& ctx, ClustererTestResult& testResult);
 
-	I writeSpotLight(const LightComponent& lightc, const FrustumComponent* lightFrc, LightBinContext& ctx);
+	void writeAndBinSpotLight(
+		const SpotLightQueueElement& lightEl, LightBinContext& ctx, ClustererTestResult& testResult);
 
-	void binLight(const SpatialComponent& sp,
-		const LightComponent& lightc,
-		U pos,
-		U lightType,
-		LightBinContext& ctx,
-		ClustererTestResult& testResult) const;
+	void writeAndBinProbe(
+		const ReflectionProbeQueueElement& probe, LightBinContext& ctx, ClustererTestResult& testResult);
 
-	void writeAndBinProbe(const SceneNode& node, LightBinContext& ctx, ClustererTestResult& testResult);
-
-	void writeAndBinDecal(const SceneNode& node, LightBinContext& ctx, ClustererTestResult& testResult);
+	void writeAndBinDecal(const DecalQueueElement& decal, LightBinContext& ctx, ClustererTestResult& testResult);
 };
 /// @}
 

+ 11 - 9
src/anki/renderer/LightShading.cpp

@@ -10,7 +10,7 @@
 #include <anki/renderer/Indirect.h>
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/LightBin.h>
-#include <anki/scene/FrustumComponent.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 
@@ -128,11 +128,11 @@ Error LightShading::binLights(RenderingContext& ctx)
 {
 	updateCommonBlock(ctx);
 
-	ANKI_CHECK(m_lightBin->bin(ctx.m_viewMat,
-		ctx.m_projMat,
-		ctx.m_viewProjMat,
-		ctx.m_camTrfMat,
-		*ctx.m_visResults,
+	ANKI_CHECK(m_lightBin->bin(ctx.m_renderQueue->m_viewMatrix,
+		ctx.m_renderQueue->m_projectionMatrix,
+		ctx.m_renderQueue->m_viewProjectionMatrix,
+		ctx.m_renderQueue->m_cameraTransform,
+		*ctx.m_renderQueue,
 		getFrameAllocator(),
 		m_maxLightIds,
 		true,
@@ -196,10 +196,12 @@ void LightShading::updateCommonBlock(RenderingContext& ctx)
 
 	// Start writing
 	blk->m_projectionParams = ctx.m_unprojParams;
-	blk->m_nearFarClustererMagicPad1 =
-		Vec4(ctx.m_near, ctx.m_far, m_lightBin->getClusterer().getShaderMagicValue(), 0.0);
+	blk->m_nearFarClustererMagicPad1 = Vec4(ctx.m_renderQueue->m_cameraNear,
+		ctx.m_renderQueue->m_cameraFar,
+		m_lightBin->getClusterer().getShaderMagicValue(),
+		0.0);
 
-	blk->m_invViewRotation = Mat3x4(ctx.m_viewMat.getInverse().getRotationPart());
+	blk->m_invViewRotation = Mat3x4(ctx.m_renderQueue->m_viewMatrix.getInverse().getRotationPart());
 
 	blk->m_rendererSizeTimePad1 = Vec4(m_r->getWidth(), m_r->getHeight(), HighRezTimer::getCurrentTime(), 0.0);
 

+ 0 - 1
src/anki/renderer/LightShading.h

@@ -14,7 +14,6 @@ namespace anki
 {
 
 // Forward
-class FrustumComponent;
 class LightBin;
 
 /// @addtogroup renderer

+ 4 - 12
src/anki/renderer/MainRenderer.cpp

@@ -10,8 +10,7 @@
 #include <anki/renderer/Dbg.h>
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/Indirect.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/Camera.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/util/Logger.h>
 #include <anki/util/File.h>
 #include <anki/util/Filesystem.h>
@@ -81,7 +80,7 @@ Error MainRenderer::create(ThreadPool* threadpool,
 	return ErrorCode::NONE;
 }
 
-Error MainRenderer::render(SceneGraph& scene)
+Error MainRenderer::render(RenderQueue& rqueue)
 {
 	ANKI_TRACE_START_EVENT(RENDER);
 
@@ -106,15 +105,8 @@ Error MainRenderer::render(SceneGraph& scene)
 	}
 
 	ctx.m_commandBuffer = cmdb;
-	const FrustumComponent& frc = scene.getActiveCamera().getComponent<FrustumComponent>();
-	ctx.m_visResults = &frc.getVisibilityTestResults();
-	ctx.m_viewMat = frc.getViewMatrix();
-	ctx.m_projMat = frc.getProjectionMatrix();
-	ctx.m_viewProjMat = frc.getViewProjectionMatrix();
-	ctx.m_camTrfMat = Mat4(frc.getFrustum().getTransform());
-	ctx.m_near = frc.getFrustum().getNear();
-	ctx.m_far = frc.getFrustum().getFar();
-	ctx.m_unprojParams = ctx.m_projMat.extractPerspectiveUnprojectionParams();
+	ctx.m_renderQueue = &rqueue;
+	ctx.m_unprojParams = ctx.m_renderQueue->m_projectionMatrix.extractPerspectiveUnprojectionParams();
 	ANKI_CHECK(m_r->render(ctx));
 
 	// Blit renderer's result to default FB if needed

+ 1 - 3
src/anki/renderer/MainRenderer.h

@@ -15,8 +15,6 @@ namespace anki
 // Forward
 class ResourceManager;
 class ConfigSet;
-class SceneGraph;
-class SceneNode;
 class ThreadPool;
 class StagingGpuMemoryManager;
 
@@ -40,7 +38,7 @@ public:
 		const ConfigSet& config,
 		Timestamp* globTimestamp);
 
-	ANKI_USE_RESULT Error render(SceneGraph& scene);
+	ANKI_USE_RESULT Error render(RenderQueue& rqueue);
 
 	Dbg& getDbg();
 

+ 6 - 2
src/anki/renderer/RenderQueue.h

@@ -67,9 +67,10 @@ class SpotLightQueueElement final
 public:
 	U64 m_uuid;
 	Mat4 m_worldTransform;
+	Mat4 m_textureMatrix;
 	F32 m_distance;
-	F32 m_outerAngleCos;
-	F32 m_innerAngleCos;
+	F32 m_outerAngle;
+	F32 m_innerAngle;
 	Vec3 m_diffuseColor;
 	Vec3 m_specularColor;
 	RenderQueue* m_shadowRenderQueue;
@@ -140,6 +141,9 @@ public:
 
 	/// Applies only if the RenderQueue holds shadow casters. It's the timesamp that modified
 	Timestamp m_shadowRenderablesLastUpdateTimestamp = 0;
+
+	F32 m_cameraNear;
+	F32 m_cameraFar;
 };
 /// @}
 

+ 14 - 19
src/anki/renderer/Renderer.cpp

@@ -4,8 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/Renderer.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/FrustumComponent.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/core/Trace.h>
 #include <anki/misc/ConfigSet.h>
 
@@ -30,10 +29,8 @@
 namespace anki
 {
 
-static Bool threadWillDoWork(
-	const RenderingContext& ctx, VisibilityGroupType typeOfWork, U32 threadId, PtrSize threadCount)
+static Bool threadWillDoWork(PtrSize problemSize, U32 threadId, PtrSize threadCount)
 {
-	U problemSize = ctx.m_visResults->getCount(typeOfWork);
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
 
@@ -236,8 +233,8 @@ Error Renderer::render(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	ctx.m_jitterMat = m_jitteredMats8x[m_frameCount & (8 - 1)];
-	ctx.m_projMatJitter = ctx.m_jitterMat * ctx.m_projMat;
-	ctx.m_viewProjMatJitter = ctx.m_projMatJitter * ctx.m_viewMat;
+	ctx.m_projMatJitter = ctx.m_jitterMat * ctx.m_renderQueue->m_projectionMatrix;
+	ctx.m_viewProjMatJitter = ctx.m_projMatJitter * ctx.m_renderQueue->m_viewMatrix;
 
 	ctx.m_prevViewProjMat = m_prevViewProjMat;
 	ctx.m_prevCamTransform = m_prevCamTransform;
@@ -255,19 +252,22 @@ Error Renderer::render(RenderingContext& ctx)
 		m_resourcesDirty = false;
 	}
 
+	// Prepare SM. Do that first because it touches the render queue elements
+	m_shadowMapping->prepareBuildCommandBuffers(ctx);
+
 	// Run stages
 	m_indirect->run(ctx);
 
 	ANKI_CHECK(m_lightShading->binLights(ctx));
-
 	m_lensFlare->resetOcclusionQueries(ctx, cmdb);
+
 	ANKI_CHECK(buildCommandBuffers(ctx));
 
 	// Barriers
 	m_shadowMapping->setPreRunBarriers(ctx);
 	m_gbuffer->setPreRunBarriers(ctx);
 
-	// Passes
+	// Passes & more
 	m_shadowMapping->run(ctx);
 	m_gbuffer->run(ctx);
 
@@ -380,8 +380,8 @@ Error Renderer::render(RenderingContext& ctx)
 	ANKI_CHECK(m_finalComposite->run(ctx));
 
 	++m_frameCount;
-	m_prevViewProjMat = ctx.m_viewProjMat;
-	m_prevCamTransform = ctx.m_camTrfMat;
+	m_prevViewProjMat = ctx.m_renderQueue->m_viewProjectionMatrix;
+	m_prevCamTransform = ctx.m_renderQueue->m_cameraTransform;
 
 	return ErrorCode::NONE;
 }
@@ -568,24 +568,19 @@ Error Renderer::buildCommandBuffers(RenderingContext& ctx)
 	ANKI_TRACE_SCOPED_EVENT(RENDERER_COMMAND_BUFFER_BUILDING);
 	ThreadPool& threadPool = getThreadPool();
 
-	// Prepare
-	if(m_shadowMapping)
-	{
-		m_shadowMapping->prepareBuildCommandBuffers(ctx);
-	}
-
 	// Find the last jobs for MS and FS
 	U32 lastMsJob = MAX_U32;
 	U32 lastFsJob = MAX_U32;
 	U threadCount = threadPool.getThreadsCount();
 	for(U i = threadCount - 1; i != 0; --i)
 	{
-		if(threadWillDoWork(ctx, VisibilityGroupType::RENDERABLES_MS, i, threadCount) && lastMsJob == MAX_U32)
+		if(threadWillDoWork(ctx.m_renderQueue->m_renderables.getSize(), i, threadCount) && lastMsJob == MAX_U32)
 		{
 			lastMsJob = i;
 		}
 
-		if(threadWillDoWork(ctx, VisibilityGroupType::RENDERABLES_FS, i, threadCount) && lastFsJob == MAX_U32)
+		if(threadWillDoWork(ctx.m_renderQueue->m_forwardShadingRenderables.getSize(), i, threadCount)
+			&& lastFsJob == MAX_U32)
 		{
 			lastFsJob = i;
 		}

+ 6 - 14
src/anki/renderer/Renderer.h

@@ -9,7 +9,6 @@
 #include <anki/renderer/Drawer.h>
 #include <anki/Math.h>
 #include <anki/Gr.h>
-#include <anki/scene/Forward.h>
 #include <anki/resource/Forward.h>
 #include <anki/resource/ShaderResource.h>
 #include <anki/core/Timestamp.h>
@@ -24,7 +23,6 @@ namespace anki
 class ConfigSet;
 class ResourceManager;
 class StagingGpuMemoryManager;
-class VisibilityTestResults;
 
 /// @addtogroup renderer
 /// @{
@@ -33,23 +31,17 @@ class VisibilityTestResults;
 class RenderingContext
 {
 public:
-	const VisibilityTestResults* m_visResults ANKI_DBG_NULLIFY;
-	Mat4 m_viewMat;
-	Mat4 m_projMat;
-	Mat4 m_viewProjMat;
+	RenderQueue* m_renderQueue ANKI_DBG_NULLIFY;
 
+	// Extra matrices
 	Mat4 m_projMatJitter;
 	Mat4 m_viewProjMatJitter;
 	Mat4 m_jitterMat;
-
-	Mat4 m_camTrfMat;
-	F32 m_near;
-	F32 m_far;
-	Vec4 m_unprojParams;
-
 	Mat4 m_prevViewProjMat;
 	Mat4 m_prevCamTransform;
 
+	Vec4 m_unprojParams;
+
 	CommandBufferPtr m_commandBuffer; ///< Primary command buffer.
 
 	StackAllocator<U8> m_tempAllocator;
@@ -101,8 +93,8 @@ public:
 		/// [casterIdx][threadIdx][faceIdx]
 		DynamicArrayAuto<CommandBufferPtr> m_omniCommandBuffers;
 
-		DynamicArrayAuto<SceneNode*> m_spots;
-		DynamicArrayAuto<SceneNode*> m_omnis;
+		DynamicArrayAuto<SpotLightQueueElement*> m_spots;
+		DynamicArrayAuto<PointLightQueueElement*> m_omnis;
 
 		ShadowMapping(const StackAllocator<U8>& alloc)
 			: m_spotFramebuffers(alloc)

+ 92 - 91
src/anki/renderer/ShadowMapping.cpp

@@ -5,12 +5,9 @@
 
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/Renderer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/core/App.h>
 #include <anki/core/Trace.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/Light.h>
-#include <anki/scene/FrustumComponent.h>
-#include <anki/scene/MoveComponent.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/ThreadPool.h>
 
@@ -112,7 +109,7 @@ Error ShadowMapping::initInternal(const ConfigSet& config)
 
 void ShadowMapping::run(RenderingContext& ctx)
 {
-	ANKI_TRACE_START_EVENT(RENDER_SM);
+	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	const U threadCount = m_r->getThreadPool().getThreadsCount();
@@ -131,6 +128,8 @@ void ShadowMapping::run(RenderingContext& ctx)
 			}
 		}
 		cmdb->endRenderPass();
+
+		ANKI_TRACE_INC_COUNTER(RENDERER_SHADOW_PASSES, 1);
 	}
 
 	// Omni lights
@@ -150,19 +149,19 @@ void ShadowMapping::run(RenderingContext& ctx)
 				}
 			}
 			cmdb->endRenderPass();
+
+			ANKI_TRACE_INC_COUNTER(RENDERER_SHADOW_PASSES, 1);
 		}
 	}
-
-	ANKI_TRACE_STOP_EVENT(RENDER_SM);
 }
 
-template<typename TShadowmap, typename TContainer>
-void ShadowMapping::bestCandidate(SceneNode& light, TContainer& arr, TShadowmap*& out)
+template<typename TLightElement, typename TShadowmap, typename TContainer>
+void ShadowMapping::bestCandidate(TLightElement& light, TContainer& arr, TShadowmap*& out)
 {
 	// Allready there
 	for(TShadowmap& sm : arr)
 	{
-		if(&light == sm.m_light)
+		if(light.m_uuid == sm.m_lightUuid)
 		{
 			out = &sm;
 			return;
@@ -172,9 +171,9 @@ void ShadowMapping::bestCandidate(SceneNode& light, TContainer& arr, TShadowmap*
 	// Find a null
 	for(TShadowmap& sm : arr)
 	{
-		if(sm.m_light == nullptr)
+		if(sm.m_lightUuid == 0)
 		{
-			sm.m_light = &light;
+			sm.m_lightUuid = light.m_uuid;
 			sm.m_timestamp = 0;
 			out = &sm;
 			return;
@@ -191,36 +190,43 @@ void ShadowMapping::bestCandidate(SceneNode& light, TContainer& arr, TShadowmap*
 		}
 	}
 
-	sm->m_light = &light;
+	sm->m_lightUuid = light.m_uuid;
 	sm->m_timestamp = 0;
 	out = sm;
 }
 
-Bool ShadowMapping::skip(SceneNode& light, ShadowmapBase& sm)
+Bool ShadowMapping::skip(PointLightQueueElement& light, ShadowmapBase& sm)
 {
-	MoveComponent* movc = light.tryGetComponent<MoveComponent>();
+	Timestamp maxTimestamp = light.m_shadowRenderQueues[0]->m_shadowRenderablesLastUpdateTimestamp;
+	for(U i = 1; i < 6; ++i)
+	{
+		maxTimestamp = max(maxTimestamp, light.m_shadowRenderQueues[i]->m_shadowRenderablesLastUpdateTimestamp);
+	}
 
-	Timestamp lastUpdate = movc->getTimestamp();
+	const Bool shouldUpdate = maxTimestamp >= sm.m_timestamp || m_r->resourcesLoaded();
+	if(shouldUpdate)
+	{
+		sm.m_timestamp = m_r->getGlobalTimestamp();
+	}
 
-	Error err = light.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) {
-		lastUpdate = max(lastUpdate, fr.getTimestamp());
+	// Update the layer ID anyway
+	light.m_textureArrayIndex = sm.m_layerId;
 
-		VisibilityTestResults& vi = fr.getVisibilityTestResults();
-		lastUpdate = max(lastUpdate, vi.getShapeUpdateTimestamp());
+	return !shouldUpdate;
+}
 
-		return ErrorCode::NONE;
-	});
-	(void)err;
+Bool ShadowMapping::skip(SpotLightQueueElement& light, ShadowmapBase& sm)
+{
+	const Timestamp maxTimestamp = light.m_shadowRenderQueue->m_shadowRenderablesLastUpdateTimestamp;
 
-	Bool shouldUpdate = lastUpdate >= sm.m_timestamp || m_r->resourcesLoaded();
+	const Bool shouldUpdate = maxTimestamp >= sm.m_timestamp || m_r->resourcesLoaded();
 	if(shouldUpdate)
 	{
 		sm.m_timestamp = m_r->getGlobalTimestamp();
 	}
 
 	// Update the layer ID anyway
-	LightComponent& lcomp = light.getComponent<LightComponent>();
-	lcomp.setShadowMapIndex(sm.m_layerId);
+	light.m_textureArrayIndex = sm.m_layerId;
 
 	return !shouldUpdate;
 }
@@ -253,11 +259,9 @@ void ShadowMapping::buildCommandBuffers(RenderingContext& ctx, U threadId, U thr
 }
 
 void ShadowMapping::doSpotLight(
-	SceneNode& light, CommandBufferPtr& cmdb, FramebufferPtr& fb, U threadId, U threadCount) const
+	const SpotLightQueueElement& light, CommandBufferPtr& cmdb, FramebufferPtr& fb, U threadId, U threadCount) const
 {
-	FrustumComponent& frc = light.getComponent<FrustumComponent>();
-	VisibilityTestResults& vis = frc.getVisibilityTestResults();
-	U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_MS);
+	const U problemSize = light.m_shadowRenderQueue->m_renderables.getSize();
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
 
@@ -277,7 +281,7 @@ void ShadowMapping::doSpotLight(
 
 	// Inform on Rts
 	cmdb->informTextureSurfaceCurrentUsage(m_spotTexArray,
-		TextureSurfaceInfo(0, 0, 0, light.getComponent<LightComponent>().getShadowMapIndex()),
+		TextureSurfaceInfo(0, 0, 0, light.m_textureArrayIndex),
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
 
 	// Set state
@@ -285,23 +289,26 @@ void ShadowMapping::doSpotLight(
 	cmdb->setPolygonOffset(1.0, 2.0);
 
 	m_r->getSceneDrawer().drawRange(Pass::SM,
-		frc.getViewMatrix(),
-		frc.getViewProjectionMatrix(),
+		light.m_shadowRenderQueue->m_viewMatrix,
+		light.m_shadowRenderQueue->m_viewProjectionMatrix,
 		cmdb,
-		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
-		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end);
+		light.m_shadowRenderQueue->m_renderables.getBegin() + start,
+		light.m_shadowRenderQueue->m_renderables.getBegin() + end);
 
 	cmdb->flush();
 }
 
-void ShadowMapping::doOmniLight(
-	SceneNode& light, CommandBufferPtr cmdbs[], Array<FramebufferPtr, 6>& fbs, U threadId, U threadCount) const
+void ShadowMapping::doOmniLight(const PointLightQueueElement& light,
+	CommandBufferPtr cmdbs[],
+	Array<FramebufferPtr, 6>& fbs,
+	U threadId,
+	U threadCount) const
 {
-	U frCount = 0;
+	for(U i = 0; i < 6; ++i)
+	{
+		const RenderQueue& rqueue = *light.m_shadowRenderQueues[i];
 
-	Error err = light.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
-		VisibilityTestResults& vis = frc.getVisibilityTestResults();
-		U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_MS);
+		const U problemSize = rqueue.m_renderables.getSize();
 		PtrSize start, end;
 		ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
 
@@ -313,32 +320,28 @@ void ShadowMapping::doOmniLight(
 			{
 				cinf.m_flags |= CommandBufferFlag::SMALL_BATCH;
 			}
-			cinf.m_framebuffer = fbs[frCount];
-			cmdbs[frCount] = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
+			cinf.m_framebuffer = fbs[i];
+			cmdbs[i] = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 
 			// Inform on Rts
-			cmdbs[frCount]->informTextureSurfaceCurrentUsage(m_omniTexArray,
-				TextureSurfaceInfo(0, 0, frCount, light.getComponent<LightComponent>().getShadowMapIndex()),
+			cmdbs[i]->informTextureSurfaceCurrentUsage(m_omniTexArray,
+				TextureSurfaceInfo(0, 0, i, light.m_textureArrayIndex),
 				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE);
 
 			// Set state
-			cmdbs[frCount]->setViewport(0, 0, m_resolution, m_resolution);
-			cmdbs[frCount]->setPolygonOffset(1.0, 2.0);
+			cmdbs[i]->setViewport(0, 0, m_resolution, m_resolution);
+			cmdbs[i]->setPolygonOffset(1.0, 2.0);
 
 			m_r->getSceneDrawer().drawRange(Pass::SM,
-				frc.getViewMatrix(),
-				frc.getViewProjectionMatrix(),
-				cmdbs[frCount],
-				vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
-				vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end);
+				rqueue.m_viewMatrix,
+				rqueue.m_viewProjectionMatrix,
+				cmdbs[i],
+				rqueue.m_renderables.getBegin() + start,
+				rqueue.m_renderables.getBegin() + end);
 
-			cmdbs[frCount]->flush();
+			cmdbs[i]->flush();
 		}
-
-		++frCount;
-		return ErrorCode::NONE;
-	});
-	(void)err;
+	}
 }
 
 void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
@@ -346,50 +349,50 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
 
 	// Gather the lights
-	const VisibilityTestResults& vi = *ctx.m_visResults;
+	RenderQueue& rqueue = *ctx.m_renderQueue;
 
 	const U MAX = 64;
-	Array<SceneNode*, MAX> spotCasters;
-	Array<SceneNode*, MAX> omniCasters;
+	Array<SpotLightQueueElement*, MAX> spotCasters;
+	Array<PointLightQueueElement*, MAX> omniCasters;
 	U spotCastersCount = 0;
 	U omniCastersCount = 0;
 
-	auto it = vi.getBegin(VisibilityGroupType::LIGHTS_POINT);
-	auto lend = vi.getEnd(VisibilityGroupType::LIGHTS_POINT);
-	for(; it != lend; ++it)
 	{
-		SceneNode* node = (*it).m_node;
-		LightComponent& light = node->getComponent<LightComponent>();
-		ANKI_ASSERT(light.getLightComponentType() == LightComponentType::POINT);
-
-		if(light.getShadowEnabled())
+		auto it = rqueue.m_pointLights.getBegin();
+		auto lend = rqueue.m_pointLights.getEnd();
+		for(; it != lend; ++it)
 		{
-			ShadowmapOmni* sm;
-			bestCandidate(*node, m_omnis, sm);
+			const Bool castsShadow = it->m_shadowRenderQueues[0] != nullptr;
 
-			if(!skip(*node, *sm))
+			if(castsShadow)
 			{
-				omniCasters[omniCastersCount++] = node;
+				ShadowmapOmni* sm;
+				bestCandidate(*it, m_omnis, sm);
+
+				if(!skip(*it, *sm))
+				{
+					omniCasters[omniCastersCount++] = &(*it);
+				}
 			}
 		}
 	}
 
-	it = vi.getBegin(VisibilityGroupType::LIGHTS_SPOT);
-	lend = vi.getEnd(VisibilityGroupType::LIGHTS_SPOT);
-	for(; it != lend; ++it)
 	{
-		SceneNode* node = (*it).m_node;
-		LightComponent& light = node->getComponent<LightComponent>();
-		ANKI_ASSERT(light.getLightComponentType() == LightComponentType::SPOT);
-
-		if(light.getShadowEnabled())
+		auto it = rqueue.m_spotLights.getBegin();
+		auto lend = rqueue.m_spotLights.getEnd();
+		for(; it != lend; ++it)
 		{
-			ShadowmapSpot* sm;
-			bestCandidate(*node, m_spots, sm);
+			const Bool castsShadow = it->m_shadowRenderQueue != nullptr;
 
-			if(!skip(*node, *sm))
+			if(castsShadow)
 			{
-				spotCasters[spotCastersCount++] = node;
+				ShadowmapSpot* sm;
+				bestCandidate(*it, m_spots, sm);
+
+				if(!skip(*it, *sm))
+				{
+					spotCasters[spotCastersCount++] = &(*it);
+				}
 			}
 		}
 	}
@@ -404,7 +407,7 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 	if(spotCastersCount > 0)
 	{
 		ctx.m_shadowMapping.m_spots.create(spotCastersCount);
-		memcpy(&ctx.m_shadowMapping.m_spots[0], &spotCasters[0], sizeof(SceneNode*) * spotCastersCount);
+		memcpy(&ctx.m_shadowMapping.m_spots[0], &spotCasters[0], sizeof(SpotLightQueueElement*) * spotCastersCount);
 
 		ctx.m_shadowMapping.m_spotCommandBuffers.create(spotCastersCount * m_r->getThreadPool().getThreadsCount());
 
@@ -418,8 +421,7 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 		ctx.m_shadowMapping.m_spotFramebuffers.create(spotCastersCount);
 		for(U i = 0; i < spotCastersCount; ++i)
 		{
-			const LightComponent& lightc = ctx.m_shadowMapping.m_spots[i]->getComponent<LightComponent>();
-			const U idx = lightc.getShadowMapIndex();
+			const U idx = ctx.m_shadowMapping.m_spots[i]->m_textureArrayIndex;
 
 			ctx.m_shadowMapping.m_spotFramebuffers[i] = m_spots[idx].m_fb;
 			ctx.m_shadowMapping.m_spotCacheIndices[i] = idx;
@@ -429,7 +431,7 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 	if(omniCastersCount > 0)
 	{
 		ctx.m_shadowMapping.m_omnis.create(omniCastersCount);
-		memcpy(&ctx.m_shadowMapping.m_omnis[0], &omniCasters[0], sizeof(SceneNode*) * omniCastersCount);
+		memcpy(&ctx.m_shadowMapping.m_omnis[0], &omniCasters[0], sizeof(PointLightQueueElement*) * omniCastersCount);
 
 		ctx.m_shadowMapping.m_omniCommandBuffers.create(omniCastersCount * 6 * m_r->getThreadPool().getThreadsCount());
 
@@ -443,8 +445,7 @@ void ShadowMapping::prepareBuildCommandBuffers(RenderingContext& ctx)
 		ctx.m_shadowMapping.m_omniFramebuffers.create(omniCastersCount);
 		for(U i = 0; i < omniCastersCount; ++i)
 		{
-			const LightComponent& lightc = ctx.m_shadowMapping.m_omnis[i]->getComponent<LightComponent>();
-			const U idx = lightc.getShadowMapIndex();
+			const U idx = ctx.m_shadowMapping.m_omnis[i]->m_textureArrayIndex;
 
 			for(U j = 0; j < 6; ++j)
 			{

+ 17 - 12
src/anki/renderer/ShadowMapping.h

@@ -13,9 +13,6 @@
 namespace anki
 {
 
-// Forward
-class SceneNode;
-
 /// @addtogroup renderer
 /// @{
 
@@ -55,8 +52,8 @@ private:
 	{
 	public:
 		U32 m_layerId;
-		SceneNode* m_light = nullptr;
 		U32 m_timestamp = 0; ///< Timestamp of last render or light change
+		U64 m_lightUuid = 0;
 	};
 
 	class ShadowmapSpot : public ShadowmapBase
@@ -83,16 +80,24 @@ private:
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 	/// Find the best shadowmap for that light
-	template<typename TShadowmap, typename TContainer>
-	void bestCandidate(SceneNode& light, TContainer& arr, TShadowmap*& out);
+	template<typename TLightElement, typename TShadowmap, typename TContainer>
+	void bestCandidate(TLightElement& light, TContainer& arr, TShadowmap*& out);
 
 	/// Check if a shadow pass can be skipped.
-	Bool skip(SceneNode& light, ShadowmapBase& sm);
-
-	void doSpotLight(SceneNode& light, CommandBufferPtr& cmdBuff, FramebufferPtr& fb, U threadId, U threadCount) const;
-
-	void doOmniLight(
-		SceneNode& light, CommandBufferPtr cmdbs[], Array<FramebufferPtr, 6>& fbs, U threadId, U threadCount) const;
+	Bool skip(PointLightQueueElement& light, ShadowmapBase& sm);
+	Bool skip(SpotLightQueueElement& light, ShadowmapBase& sm);
+
+	void doSpotLight(const SpotLightQueueElement& light,
+		CommandBufferPtr& cmdBuff,
+		FramebufferPtr& fb,
+		U threadId,
+		U threadCount) const;
+
+	void doOmniLight(const PointLightQueueElement& light,
+		CommandBufferPtr cmdbs[],
+		Array<FramebufferPtr, 6>& fbs,
+		U threadId,
+		U threadCount) const;
 };
 
 /// @}

+ 4 - 4
src/anki/renderer/Ssao.cpp

@@ -6,11 +6,10 @@
 #include <anki/renderer/Ssao.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/GBuffer.h>
+#include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/DepthDownscale.h>
-#include <anki/scene/SceneGraph.h>
 #include <anki/util/Functions.h>
 #include <anki/misc/ConfigSet.h>
-#include <anki/scene/FrustumComponent.h>
 
 namespace anki
 {
@@ -69,11 +68,12 @@ void SsaoMain::run(RenderingContext& ctx)
 	};
 
 	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 0);
-	const Mat4& pmat = ctx.m_projMat;
+	const Mat4& pmat = ctx.m_renderQueue->m_projectionMatrix;
 	unis->m_unprojectionParams = ctx.m_unprojParams;
 	unis->m_projectionMat = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
 	unis->m_noiseLayerPad3 = Vec4(m_r->getFrameCount() % m_noiseTex->getLayerCount(), 0.0, 0.0, 0.0);
-	unis->m_prevViewProjMatMulInvViewProjMat = ctx.m_prevViewProjMat * ctx.m_viewProjMat.getInverse();
+	unis->m_prevViewProjMatMulInvViewProjMat =
+		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
 
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();

+ 5 - 4
src/anki/renderer/Volumetric.cpp

@@ -9,7 +9,7 @@
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/LightBin.h>
-#include <anki/scene/FrustumComponent.h>
+#include <anki/renderer/RenderQueue.h>
 
 namespace anki
 {
@@ -103,15 +103,16 @@ void VolumetricMain::run(RenderingContext& ctx)
 	};
 
 	Unis* uniforms = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 3);
-	computeLinearizeDepthOptimal(ctx.m_near,
-		ctx.m_far,
+	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear,
+		ctx.m_renderQueue->m_cameraFar,
 		uniforms->m_linearizeNoiseTexOffsetLayer.x(),
 		uniforms->m_linearizeNoiseTexOffsetLayer.y());
 	F32 texelOffset = 1.0 / m_noiseTex->getWidth();
 	uniforms->m_linearizeNoiseTexOffsetLayer.z() = m_r->getFrameCount() * texelOffset;
 	uniforms->m_linearizeNoiseTexOffsetLayer.w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
 	uniforms->m_fogParticleColorPad1 = Vec4(m_fogParticleColor, 0.0);
-	uniforms->m_prevViewProjMatMulInvViewProjMat = ctx.m_prevViewProjMat * ctx.m_viewProjMat.getInverse();
+	uniforms->m_prevViewProjMatMulInvViewProjMat =
+		ctx.m_prevViewProjMat * ctx.m_renderQueue->m_viewProjectionMatrix.getInverse();
 
 	bindStorage(cmdb, 0, 0, ctx.m_lightShading.m_clustersToken);
 	bindStorage(cmdb, 0, 1, ctx.m_lightShading.m_lightIndicesToken);

+ 0 - 1
src/anki/scene/Forward.h

@@ -29,7 +29,6 @@ class Path;
 
 // Other
 class SceneGraph;
-class VisibleNode;
 class Sector;
 
 } // end namespace anki

+ 0 - 11
src/anki/scene/FrustumComponent.cpp

@@ -22,19 +22,8 @@ FrustumComponent::FrustumComponent(SceneNode* node, Frustum* frustum)
 	setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 }
 
-void FrustumComponent::setVisibilityTestResults(VisibilityTestResults* visible)
-{
-	ANKI_ASSERT(m_visible == nullptr);
-	m_visible = visible;
-
-	m_stats.m_renderablesCount = visible->getCount(VisibilityGroupType::RENDERABLES_MS);
-	m_stats.m_lightsCount = visible->getCount(VisibilityGroupType::LIGHTS_POINT);
-}
-
 Error FrustumComponent::update(SceneNode& node, F32, F32, Bool& updated)
 {
-	m_visible = nullptr;
-
 	updated = false;
 
 	if(m_flags.get(SHAPE_MARKED_FOR_UPDATE))

+ 0 - 33
src/anki/scene/FrustumComponent.h

@@ -14,9 +14,6 @@
 namespace anki
 {
 
-// Forward
-class VisibilityTestResults;
-
 /// @addtogroup scene
 /// @{
 
@@ -86,31 +83,6 @@ public:
 		return m_frustum->getTransform().getOrigin();
 	}
 
-	void setVisibilityTestResults(VisibilityTestResults* visible);
-
-	/// Call this after the tests. Before it will point to junk
-	VisibilityTestResults& getVisibilityTestResults()
-	{
-		ANKI_ASSERT(m_visible != nullptr);
-		return *m_visible;
-	}
-
-	const VisibilityTestResults& getVisibilityTestResults() const
-	{
-		ANKI_ASSERT(m_visible != nullptr);
-		return *m_visible;
-	}
-
-	Bool hasVisibilityTestResults() const
-	{
-		return m_visible != nullptr;
-	}
-
-	const VisibilityStats& getLastVisibilityStats() const
-	{
-		return m_stats;
-	}
-
 	/// Call when the shape of the frustum got changed.
 	void markShapeForUpdate()
 	{
@@ -181,11 +153,6 @@ private:
 	Mat4 m_vm = Mat4::getIdentity(); ///< View matrix
 	Mat4 m_vpm = Mat4::getIdentity(); ///< View projection matrix
 
-	/// Visibility stuff. It's per frame so the pointer is invalid on the next frame and before any visibility tests
-	/// are run.
-	VisibilityTestResults* m_visible = nullptr;
-	VisibilityStats m_stats;
-
 	BitMask<U16> m_flags;
 };
 /// @}

+ 25 - 4
src/anki/scene/Light.cpp

@@ -13,10 +13,10 @@ namespace anki
 {
 
 /// Feedback component.
-class LightFeedbackComponent : public SceneComponent
+class Light::MovedFeedbackComponent : public SceneComponent
 {
 public:
-	LightFeedbackComponent(SceneNode* node)
+	MovedFeedbackComponent(SceneNode* node)
 		: SceneComponent(SceneComponentType::NONE, node)
 	{
 	}
@@ -33,6 +33,24 @@ public:
 			lnode.onMoveUpdate(move);
 		}
 
+		return ErrorCode::NONE;
+	}
+};
+
+/// Feedback component.
+class Light::LightChangedFeedbackComponent : public SceneComponent
+{
+public:
+	LightChangedFeedbackComponent(SceneNode* node)
+		: SceneComponent(SceneComponentType::NONE, node)
+	{
+	}
+
+	Error update(SceneNode& node, F32, F32, Bool& updated) override
+	{
+		updated = false;
+		Light& lnode = static_cast<Light&>(node);
+
 		LightComponent& light = node.getComponent<LightComponent>();
 		if(light.getTimestamp() == node.getGlobalTimestamp())
 		{
@@ -58,11 +76,14 @@ Error Light::init(LightComponentType type, CollisionShape* shape)
 	// Move component
 	newComponent<MoveComponent>(this);
 
+	// Feedback component
+	newComponent<MovedFeedbackComponent>(this);
+
 	// Light component
 	newComponent<LightComponent>(this, type);
 
 	// Feedback component
-	newComponent<LightFeedbackComponent>(this);
+	newComponent<LightChangedFeedbackComponent>(this);
 
 	// Spatial component
 	newComponent<SpatialComponent>(this, shape);
@@ -106,7 +127,7 @@ void Light::onMoveUpdateCommon(MoveComponent& move)
 	}
 
 	// Update light component
-	getComponent<LightComponent>().setWorldTransform(move.getWorldTransform());
+	getComponent<LightComponent>().updateWorldTransform(move.getWorldTransform());
 }
 
 void Light::onShapeUpdateCommon(LightComponent& light)

+ 5 - 3
src/anki/scene/Light.h

@@ -20,8 +20,6 @@ namespace anki
 /// Light scene node. It can be spot or point.
 class Light : public SceneNode
 {
-	friend class LightFeedbackComponent;
-
 public:
 	Light(SceneGraph* scene, CString name);
 
@@ -43,6 +41,10 @@ protected:
 	virtual void onMoveUpdate(MoveComponent& move) = 0;
 
 	virtual void onShapeUpdate(LightComponent& light) = 0;
+
+private:
+	class MovedFeedbackComponent;
+	class LightChangedFeedbackComponent;
 };
 
 /// Point light
@@ -56,7 +58,7 @@ public:
 
 	ANKI_USE_RESULT Error frameUpdate(F32 prevUpdateTime, F32 crntTime) override;
 
-public:
+private:
 	class ShadowCombo
 	{
 	public:

+ 16 - 4
src/anki/scene/LightComponent.cpp

@@ -19,16 +19,28 @@ LightComponent::LightComponent(SceneNode* node, LightComponentType type)
 
 Error LightComponent::update(SceneNode&, F32, F32, Bool& updated)
 {
-	if(m_dirty)
+	updated = false;
+
+	if(m_flags.get(DIRTY))
 	{
 		updated = true;
-		m_dirty = false;
 	}
-	else
+
+	if(m_flags.get(TRF_DIRTY))
 	{
-		updated = false;
+		updated = true;
+
+		if(m_type == LightComponentType::SPOT)
+		{
+			static const Mat4 biasMat4(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0);
+			Mat4 proj =
+				Mat4::calculatePerspectiveProjectionMatrix(m_outerAngle, m_outerAngle, FRUSTUM_NEAR_PLANE, m_distance);
+			m_spotTextureMatrix = biasMat4 * proj * Mat4(m_trf.getInverse());
+		}
 	}
 
+	m_flags.unset(DIRTY | TRF_DIRTY);
+
 	return ErrorCode::NONE;
 }
 

+ 24 - 40
src/anki/scene/LightComponent.h

@@ -39,14 +39,10 @@ public:
 		return m_type;
 	}
 
-	const Transform& getWorldTransform() const
+	void updateWorldTransform(const Transform& trf)
 	{
-		return m_worldTrf;
-	}
-
-	void setWorldTransform(const Transform& trf)
-	{
-		m_worldTrf = trf;
+		m_trf = trf;
+		m_flags.set(TRF_DIRTY);
 	}
 
 	const Vec4& getDiffuseColor() const
@@ -72,7 +68,7 @@ public:
 	void setRadius(F32 x)
 	{
 		m_radius = x;
-		m_dirty = true;
+		m_flags.set(DIRTY);
 	}
 
 	F32 getRadius() const
@@ -83,7 +79,7 @@ public:
 	void setDistance(F32 x)
 	{
 		m_distance = x;
-		m_dirty = true;
+		m_flags.set(DIRTY);
 	}
 
 	F32 getDistance() const
@@ -95,7 +91,7 @@ public:
 	{
 		m_innerAngleCos = cos(ang / 2.0);
 		m_innerAngle = ang;
-		m_dirty = true;
+		m_flags.set(DIRTY);
 	}
 
 	F32 getInnerAngleCos() const
@@ -112,7 +108,7 @@ public:
 	{
 		m_outerAngleCos = cos(ang / 2.0);
 		m_outerAngle = ang;
-		m_dirty = true;
+		m_flags.set(DIRTY);
 	}
 
 	F32 getOuterAngle() const
@@ -127,23 +123,12 @@ public:
 
 	Bool getShadowEnabled() const
 	{
-		return m_shadow;
+		return m_flags.get(SHADOW);
 	}
 
 	void setShadowEnabled(const Bool x)
 	{
-		m_shadow = x;
-	}
-
-	U getShadowMapIndex() const
-	{
-		return static_cast<U>(m_shadowMapIndex);
-	}
-
-	void setShadowMapIndex(const U i)
-	{
-		ANKI_ASSERT(i < 0xFF);
-		m_shadowMapIndex = static_cast<U8>(i);
+		m_flags.set(SHADOW, x);
 	}
 
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
@@ -152,35 +137,27 @@ public:
 	{
 		ANKI_ASSERT(m_type == LightComponentType::POINT);
 		el.m_uuid = getUuid();
-		el.m_worldPosition = m_worldTrf.getOrigin().xyz();
+		el.m_worldPosition = m_trf.getOrigin().xyz();
 		el.m_radius = m_radius;
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_specularColor = m_specColor.xyz();
-#if ANKI_EXTRA_CHECKS
-		el.m_shadowRenderQueues = {{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
-		el.m_textureArrayIndex = MAX_U32;
-#endif
 	}
 
 	void setupSpotLightQueueElement(SpotLightQueueElement& el) const
 	{
 		ANKI_ASSERT(m_type == LightComponentType::SPOT);
 		el.m_uuid = getUuid();
-		el.m_worldTransform = Mat4(m_worldTrf);
+		el.m_worldTransform = Mat4(m_trf);
+		el.m_textureMatrix = m_spotTextureMatrix;
 		el.m_distance = m_distance;
-		el.m_outerAngleCos = m_outerAngleCos;
-		el.m_innerAngleCos = m_innerAngleCos;
+		el.m_outerAngle = m_outerAngle;
+		el.m_innerAngle = m_innerAngle;
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_specularColor = m_specColor.xyz();
-#if ANKI_EXTRA_CHECKS
-		el.m_shadowRenderQueue = nullptr;
-		el.m_textureArrayIndex = MAX_U32;
-#endif
 	}
 
 private:
 	LightComponentType m_type;
-	Transform m_worldTrf = Transform(Vec4(0.0f), Mat3x4::getIdentity(), 1.0f);
 	Vec4 m_diffColor = Vec4(0.5f);
 	Vec4 m_specColor = Vec4(0.5f);
 	union
@@ -193,10 +170,17 @@ private:
 	F32 m_outerAngle;
 	F32 m_innerAngle;
 
-	Bool8 m_shadow = false;
-	U8 m_shadowMapIndex = 0xFF; ///< Used by the renderer
+	Transform m_trf = Transform::getIdentity();
+	Mat4 m_spotTextureMatrix = Mat4::getIdentity();
+
+	enum
+	{
+		SHADOW = 1 << 0,
+		DIRTY = 1 << 1,
+		TRF_DIRTY = 1 << 2
+	};
 
-	Bool8 m_dirty = true;
+	BitMask<U8> m_flags = BitMask<U8>(DIRTY | TRF_DIRTY);
 };
 /// @}
 

+ 0 - 3
src/anki/scene/ReflectionProbeComponent.h

@@ -75,10 +75,7 @@ public:
 		el.m_uuid = getUuid();
 		el.m_worldPosition = m_pos.xyz();
 		el.m_radius = m_radius;
-#if ANKI_EXTRA_CHECKS
-		el.m_renderQueues = {{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
 		el.m_textureArrayIndex = MAX_U32;
-#endif
 	}
 
 private:

+ 1 - 1
src/anki/scene/SceneComponent.h

@@ -106,7 +106,7 @@ public:
 
 protected:
 	SceneNode* m_node = nullptr;
-	Timestamp m_timestamp; ///< Indicates when an update happened
+	Timestamp m_timestamp = 0; ///< Indicates when an update happened
 
 private:
 	SceneComponentType m_type;

+ 12 - 6
src/anki/scene/SceneGraph.cpp

@@ -188,7 +188,7 @@ void SceneGraph::deleteNodesMarkedForDeletion()
 Error SceneGraph::update(F32 prevUpdateTime, F32 crntTime)
 {
 	ANKI_ASSERT(m_mainCam);
-	ANKI_TRACE_START_EVENT(SCENE_UPDATE);
+	ANKI_TRACE_SCOPED_EVENT(SCENE_UPDATE);
 
 	m_timestamp = *m_globalTimestamp;
 
@@ -231,12 +231,14 @@ Error SceneGraph::update(F32 prevUpdateTime, F32 crntTime)
 	ANKI_CHECK(threadPool.waitForAllThreadsToFinish());
 	ANKI_TRACE_STOP_EVENT(SCENE_NODES_UPDATE);
 
-	doVisibilityTests(*m_mainCam, *this);
-
-	ANKI_TRACE_STOP_EVENT(SCENE_UPDATE);
 	return ErrorCode::NONE;
 }
 
+void SceneGraph::doVisibilityTests(RenderQueue& rqueue)
+{
+	anki::doVisibilityTests(*m_mainCam, *this, rqueue);
+}
+
 Error SceneGraph::updateNode(F32 prevTime, F32 crntTime, SceneNode& node)
 {
 	ANKI_TRACE_INC_COUNTER(SCENE_NODES_UPDATED, 1);
@@ -244,9 +246,13 @@ Error SceneGraph::updateNode(F32 prevTime, F32 crntTime, SceneNode& node)
 	Error err = ErrorCode::NONE;
 
 	// Components update
+	Timestamp componentTimestam = 0;
 	err = node.iterateComponents([&](SceneComponent& comp) -> Error {
 		Bool updated = false;
-		return comp.updateReal(node, prevTime, crntTime, updated);
+		Error e = comp.updateReal(node, prevTime, crntTime, updated);
+		componentTimestam = max(componentTimestam, comp.getTimestamp());
+
+		return e;
 	});
 
 	// Update children
@@ -258,7 +264,7 @@ Error SceneGraph::updateNode(F32 prevTime, F32 crntTime, SceneNode& node)
 	// Frame update
 	if(!err)
 	{
-		err = node.frameUpdateComplete(prevTime, crntTime);
+		err = node.frameUpdateComplete(prevTime, crntTime, componentTimestam);
 	}
 
 	return err;

+ 2 - 0
src/anki/scene/SceneGraph.h

@@ -121,6 +121,8 @@ public:
 
 	ANKI_USE_RESULT Error update(F32 prevUpdateTime, F32 crntTime);
 
+	void doVisibilityTests(RenderQueue& rqueue);
+
 	SceneNode& findSceneNode(const CString& name);
 	SceneNode* tryFindSceneNode(const CString& name);
 

+ 9 - 1
src/anki/scene/SceneNode.h

@@ -71,6 +71,11 @@ public:
 
 	Timestamp getGlobalTimestamp() const;
 
+	Timestamp getComponentMaxTimestamp() const
+	{
+		return m_maxComponentTimestamp;
+	}
+
 	SceneAllocator<U8> getSceneAllocator() const;
 
 	SceneFrameAllocator<U8> getFrameAllocator() const;
@@ -90,9 +95,10 @@ public:
 		return ErrorCode::NONE;
 	}
 
-	ANKI_USE_RESULT Error frameUpdateComplete(F32 prevUpdateTime, F32 crntTime)
+	ANKI_USE_RESULT Error frameUpdateComplete(F32 prevUpdateTime, F32 crntTime, Timestamp maxComponentTimestamp)
 	{
 		m_sectorVisitedBitset.unsetAll();
+		m_maxComponentTimestamp = maxComponentTimestamp;
 		return frameUpdate(prevUpdateTime, crntTime);
 	}
 
@@ -234,6 +240,8 @@ private:
 
 	U64 m_uuid;
 
+	Timestamp m_maxComponentTimestamp = 0;
+
 	void cacheImportantComponents();
 };
 /// @}

+ 165 - 160
src/anki/scene/Visibility.cpp

@@ -9,6 +9,7 @@
 #include <anki/scene/Sector.h>
 #include <anki/scene/FrustumComponent.h>
 #include <anki/scene/LensFlareComponent.h>
+#include <anki/scene/RenderComponent.h>
 #include <anki/scene/ReflectionProbeComponent.h>
 #include <anki/scene/ReflectionProxyComponent.h>
 #include <anki/scene/OccluderComponent.h>
@@ -23,7 +24,7 @@
 namespace anki
 {
 
-void VisibilityContext::submitNewWork(FrustumComponent& frc, ThreadHive& hive)
+void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue, ThreadHive& hive)
 {
 	// Check enabled and make sure that the results are null (this can happen on multiple on circular viewing)
 	if(ANKI_UNLIKELY(!frc.anyVisibilityTestEnabled()))
@@ -31,6 +32,13 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, ThreadHive& hive)
 		return;
 	}
 
+	rqueue.m_cameraTransform = Mat4(frc.getFrustum().getTransform());
+	rqueue.m_viewMatrix = frc.getViewMatrix();
+	rqueue.m_projectionMatrix = frc.getProjectionMatrix();
+	rqueue.m_viewProjectionMatrix = frc.getViewProjectionMatrix();
+	rqueue.m_cameraNear = frc.getFrustum().getNear();
+	rqueue.m_cameraFar = frc.getFrustum().getFar();
+
 	auto alloc = m_scene->getFrameAllocator();
 
 	// Check if this frc was tested before
@@ -97,7 +105,7 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, ThreadHive& hive)
 		}
 	}
 
-	// Gather task
+	// Gather visibles from sector
 	GatherVisiblesFromSectorsTask* gather = alloc.newInstance<GatherVisiblesFromSectorsTask>();
 	gather->m_visCtx = this;
 	gather->m_frc = &frc;
@@ -140,6 +148,7 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, ThreadHive& hive)
 	CombineResultsTask* combine = alloc.newInstance<CombineResultsTask>();
 	combine->m_visCtx = this;
 	combine->m_frc = &frc;
+	combine->m_results = &rqueue;
 	combine->m_tests = tests;
 
 	ThreadHiveTask combineTask;
@@ -157,7 +166,7 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, ThreadHive& hive)
 
 void GatherVisibleTrianglesTask::gather()
 {
-	ANKI_TRACE_START_EVENT(SCENE_VISIBILITY_GATHER_TRIANGLES);
+	ANKI_TRACE_SCOPED_EVENT(SCENE_VISIBILITY_GATHER_TRIANGLES);
 
 	auto alloc = m_visCtx->m_scene->getFrameAllocator();
 	m_verts.create(alloc, TRIANGLES_INITIAL_SIZE);
@@ -187,13 +196,11 @@ void GatherVisibleTrianglesTask::gather()
 
 	m_r.init(alloc);
 	m_r.prepare(m_frc->getViewMatrix(), m_frc->getProjectionMatrix(), 80, 50);
-
-	ANKI_TRACE_STOP_EVENT(SCENE_VISIBILITY_GATHER_TRIANGLES);
 }
 
 void RasterizeTrianglesTask::rasterize()
 {
-	ANKI_TRACE_START_EVENT(SCENE_VISIBILITY_RASTERIZE);
+	ANKI_TRACE_SCOPED_EVENT(SCENE_VISIBILITY_RASTERIZE);
 
 	PtrSize start, end;
 	ThreadPoolTask::choseStartEnd(m_taskIdx, m_taskCount, m_gatherTask->m_vertCount / 3, start, end);
@@ -206,13 +213,11 @@ void RasterizeTrianglesTask::rasterize()
 
 		m_gatherTask->m_r.draw(&first[0][0], count, sizeof(Vec3), false);
 	}
-
-	ANKI_TRACE_STOP_EVENT(SCENE_VISIBILITY_RASTERIZE);
 }
 
 void VisibilityTestTask::test(ThreadHive& hive)
 {
-	ANKI_TRACE_START_EVENT(SCENE_VISIBILITY_TEST);
+	ANKI_TRACE_SCOPED_EVENT(SCENE_VISIBILITY_TEST);
 
 	FrustumComponent& testedFrc = *m_frc;
 	ANKI_ASSERT(testedFrc.anyVisibilityTestEnabled());
@@ -220,11 +225,6 @@ void VisibilityTestTask::test(ThreadHive& hive)
 	SceneNode& testedNode = testedFrc.getSceneNode();
 	auto alloc = m_visCtx->m_scene->getFrameAllocator();
 
-	// Init test results
-	VisibilityTestResults* visible = alloc.newInstance<VisibilityTestResults>();
-	visible->create(alloc);
-	m_result = visible;
-
 	Bool wantsRenderComponents =
 		testedFrc.visibilityTestsEnabled(FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS);
 
@@ -336,6 +336,8 @@ void VisibilityTestTask::test(ThreadHive& hive)
 			return;
 		}
 
+		ANKI_ASSERT(count == 1 && "TODO: Support sub-spatials");
+
 		// Sort sub-spatials
 		Vec4 origin = testedFrc.getFrustumOrigin();
 		std::sort(sps.begin(), sps.begin() + count, [origin](const SpatialTemp& a, const SpatialTemp& b) -> Bool {
@@ -348,240 +350,243 @@ void VisibilityTestTask::test(ThreadHive& hive)
 			return dist0 < dist1;
 		});
 
-		// Update the visibleNode
-		VisibleNode visibleNode;
-		visibleNode.m_node = &node;
-
-		// Compute distance from the frustum
-		const Plane& nearPlane = testedFrc.getFrustum().getPlanesWorldSpace()[FrustumPlaneType::NEAR];
-		visibleNode.m_frustumDistance = max(0.0f, sps[0].m_sp->getAabb().testPlane(nearPlane));
-
-		ANKI_ASSERT(count < MAX_U8);
-		visibleNode.m_spatialsCount = count;
-		visibleNode.m_spatialIndices = alloc.newArray<U8>(count);
-
-		for(U i = 0; i < count; i++)
-		{
-			visibleNode.m_spatialIndices[i] = sps[i].m_idx;
-		}
+		WeakArray<RenderQueue> nextQueues;
 
 		if(rc)
 		{
 			if(wantsRenderComponents || (wantsShadowCasters && rc->getCastsShadow()))
 			{
-				visible->moveBack(alloc,
-					rc->getMaterial().isForwardShading() ? VisibilityGroupType::RENDERABLES_FS
-														 : VisibilityGroupType::RENDERABLES_MS,
-					visibleNode);
-
-				if(wantsShadowCasters)
+				RenderableQueueElement* el;
+				if(rc->getMaterial().isForwardShading())
+				{
+					el = m_result.m_forwardShadingRenderables.newElement(alloc);
+				}
+				else
 				{
-					updateTimestamp(node);
+					el = m_result.m_renderables.newElement(alloc);
 				}
+
+				rc->setupRenderableQueueElement(*el);
+
+				// Compute distance from the frustum
+				const Plane& nearPlane = testedFrc.getFrustum().getPlanesWorldSpace()[FrustumPlaneType::NEAR];
+				el->m_distanceFromCamera = max(0.0f, sps[0].m_sp->getAabb().testPlane(nearPlane));
 			}
 		}
 
 		if(lc && wantsLightComponents)
 		{
-			VisibilityGroupType gt;
 			switch(lc->getLightComponentType())
 			{
 			case LightComponentType::POINT:
-				gt = VisibilityGroupType::LIGHTS_POINT;
+			{
+				PointLightQueueElement* el = m_result.m_pointLights.newElement(alloc);
+				lc->setupPointLightQueueElement(*el);
+
+				if(lc->getShadowEnabled())
+				{
+					RenderQueue* a = alloc.newArray<RenderQueue>(6);
+					nextQueues = WeakArray<RenderQueue>(a, 6);
+
+					el->m_shadowRenderQueues[0] = &nextQueues[0];
+					el->m_shadowRenderQueues[1] = &nextQueues[1];
+					el->m_shadowRenderQueues[2] = &nextQueues[2];
+					el->m_shadowRenderQueues[3] = &nextQueues[3];
+					el->m_shadowRenderQueues[4] = &nextQueues[4];
+					el->m_shadowRenderQueues[5] = &nextQueues[5];
+				}
+				else
+				{
+					zeroMemory(el->m_shadowRenderQueues);
+				}
+
 				break;
+			}
 			case LightComponentType::SPOT:
-				gt = VisibilityGroupType::LIGHTS_SPOT;
+			{
+				SpotLightQueueElement* el = m_result.m_spotLights.newElement(alloc);
+				lc->setupSpotLightQueueElement(*el);
+
+				if(lc->getShadowEnabled())
+				{
+					RenderQueue* a = alloc.newInstance<RenderQueue>();
+					nextQueues = WeakArray<RenderQueue>(a, 1);
+					el->m_shadowRenderQueue = a;
+				}
+				else
+				{
+					el->m_shadowRenderQueue = nullptr;
+				}
+
 				break;
+			}
 			default:
 				ANKI_ASSERT(0);
-				gt = VisibilityGroupType::TYPE_COUNT;
 			}
-
-			visible->moveBack(alloc, gt, visibleNode);
 		}
 
 		if(lfc && wantsFlareComponents)
 		{
-			visible->moveBack(alloc, VisibilityGroupType::FLARES, visibleNode);
+			LensFlareQueueElement* el = m_result.m_lensFlares.newElement(alloc);
+			lfc->setupLensFlareQueueElement(*el);
 		}
 
 		if(reflc && wantsReflectionProbes)
 		{
-			visible->moveBack(alloc, VisibilityGroupType::REFLECTION_PROBES, visibleNode);
+			ReflectionProbeQueueElement* el = m_result.m_reflectionProbes.newElement(alloc);
+			reflc->setupReflectionProbeQueueElement(*el);
+
+			if(reflc->getMarkedForRendering())
+			{
+				RenderQueue* a = alloc.newArray<RenderQueue>(6);
+				nextQueues = WeakArray<RenderQueue>(a, 6);
+
+				el->m_renderQueues[0] = &nextQueues[0];
+				el->m_renderQueues[1] = &nextQueues[1];
+				el->m_renderQueues[2] = &nextQueues[2];
+				el->m_renderQueues[3] = &nextQueues[3];
+				el->m_renderQueues[4] = &nextQueues[4];
+				el->m_renderQueues[5] = &nextQueues[5];
+			}
+			else
+			{
+				el->m_renderQueues = {{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
+			}
 		}
 
 		if(proxyc && wantsReflectionProxies)
 		{
-			visible->moveBack(alloc, VisibilityGroupType::REFLECTION_PROXIES, visibleNode);
+			ANKI_ASSERT(!"TODO");
 		}
 
 		if(decalc && wantsDecals)
 		{
-			visible->moveBack(alloc, VisibilityGroupType::DECALS, visibleNode);
+			DecalQueueElement* el = m_result.m_decals.newElement(alloc);
+			decalc->setupDecalQueueElement(*el);
 		}
 
 		// Add more frustums to the list
-		err = node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
-			m_visCtx->submitNewWork(frc, hive);
-			return ErrorCode::NONE;
-		});
-		(void)err;
-	}); // end for
-
-	ANKI_TRACE_STOP_EVENT(SCENE_VISIBILITY_TEST);
-}
-
-void VisibilityTestTask::updateTimestamp(const SceneNode& node)
-{
-	Timestamp lastUpdate = 0;
-
-	const FrustumComponent* bfr = node.tryGetComponent<FrustumComponent>();
-	if(bfr)
-	{
-		lastUpdate = max(lastUpdate, bfr->getTimestamp());
-	}
-
-	const MoveComponent* bmov = node.tryGetComponent<MoveComponent>();
-	if(bmov)
-	{
-		lastUpdate = max(lastUpdate, bmov->getTimestamp());
-	}
-
-	const SpatialComponent* sp = node.tryGetComponent<SpatialComponent>();
-	if(sp)
-	{
-		lastUpdate = max(lastUpdate, sp->getTimestamp());
-	}
+		if(nextQueues.getSize() > 0)
+		{
+			count = 0;
+			err = node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
+				m_visCtx->submitNewWork(frc, nextQueues[count++], hive);
+				return ErrorCode::NONE;
+			});
+			(void)err;
+		}
 
-	m_timestamp = max(m_timestamp, lastUpdate);
+		// Update timestamp
+		m_timestamp = max(m_timestamp, node.getComponentMaxTimestamp());
+	}); // end for
 }
 
 void CombineResultsTask::combine()
 {
-	ANKI_TRACE_START_EVENT(SCENE_VISIBILITY_COMBINE_RESULTS);
+	ANKI_TRACE_SCOPED_EVENT(SCENE_VISIBILITY_COMBINE_RESULTS);
 
 	auto alloc = m_visCtx->m_scene->getFrameAllocator();
 
-	// Prepare
-	Array<VisibilityTestResults*, 32> rezArr;
-	Timestamp timestamp = 0;
+	m_results->m_shadowRenderablesLastUpdateTimestamp = 0;
 	for(U i = 0; i < m_tests.getSize(); ++i)
 	{
-		rezArr[i] = m_tests[i].m_result;
-		timestamp = max(timestamp, m_tests[i].m_timestamp);
+		m_results->m_shadowRenderablesLastUpdateTimestamp =
+			max(m_results->m_shadowRenderablesLastUpdateTimestamp, m_tests[i].m_timestamp);
 	}
 
-	WeakArray<VisibilityTestResults*> rez(&rezArr[0], m_tests.getSize());
+#define ANKI_VIS_COMBINE(t_, member_)                                                       \
+	{                                                                                       \
+		Array<TRenderQueueElementStorage<t_>*, 64> subStorages;                             \
+		for(U i = 0; i < m_tests.getSize(); ++i)                                            \
+		{                                                                                   \
+			subStorages[i] = &m_tests[i].m_result.member_;                                  \
+		}                                                                                   \
+		combineQueueElements(alloc,                                                         \
+			WeakArray<TRenderQueueElementStorage<t_>*>(&subStorages[0], m_tests.getSize()), \
+			m_results->member_);                                                            \
+	}
 
-	// Create the new combined results
-	VisibilityTestResults* visible = alloc.newInstance<VisibilityTestResults>();
-	visible->combineWith(alloc, rez);
-	visible->setShapeUpdateTimestamp(timestamp);
+	ANKI_VIS_COMBINE(RenderableQueueElement, m_renderables);
+	ANKI_VIS_COMBINE(RenderableQueueElement, m_forwardShadingRenderables);
+	ANKI_VIS_COMBINE(PointLightQueueElement, m_pointLights);
+	ANKI_VIS_COMBINE(SpotLightQueueElement, m_spotLights);
+	ANKI_VIS_COMBINE(ReflectionProbeQueueElement, m_reflectionProbes);
+	ANKI_VIS_COMBINE(LensFlareQueueElement, m_lensFlares);
+	ANKI_VIS_COMBINE(DecalQueueElement, m_decals);
 
-	// Set the frustumable
-	m_frc->setVisibilityTestResults(visible);
+#undef ANKI_VIS_COMBINE
 
 	// Sort some of the arrays
-	DistanceSortFunctor comp;
-	std::sort(visible->getBegin(VisibilityGroupType::RENDERABLES_MS),
-		visible->getEnd(VisibilityGroupType::RENDERABLES_MS),
-		comp);
-
-	std::sort(visible->getBegin(VisibilityGroupType::RENDERABLES_FS),
-		visible->getEnd(VisibilityGroupType::RENDERABLES_FS),
-		RevDistanceSortFunctor());
-
-	std::sort(visible->getBegin(VisibilityGroupType::REFLECTION_PROBES),
-		visible->getEnd(VisibilityGroupType::REFLECTION_PROBES),
-		comp);
+	std::sort(m_results->m_renderables.getBegin(),
+		m_results->m_renderables.getEnd(),
+		DistanceSortFunctor<RenderableQueueElement>());
 
-	ANKI_TRACE_STOP_EVENT(SCENE_VISIBILITY_COMBINE_RESULTS);
+	std::sort(m_results->m_forwardShadingRenderables.getBegin(),
+		m_results->m_forwardShadingRenderables.getEnd(),
+		RevDistanceSortFunctor<RenderableQueueElement>());
 }
 
-void VisibilityTestResults::create(SceneFrameAllocator<U8> alloc)
+template<typename T>
+void CombineResultsTask::combineQueueElements(
+	SceneFrameAllocator<U8>& alloc, WeakArray<TRenderQueueElementStorage<T>*> subStorages, WeakArray<T>& combined)
 {
-	m_groups[VisibilityGroupType::RENDERABLES_MS].m_nodes.create(alloc, 64);
-	m_groups[VisibilityGroupType::RENDERABLES_FS].m_nodes.create(alloc, 8);
-	m_groups[VisibilityGroupType::LIGHTS_POINT].m_nodes.create(alloc, 8);
-	m_groups[VisibilityGroupType::LIGHTS_SPOT].m_nodes.create(alloc, 4);
-}
-
-void VisibilityTestResults::moveBack(SceneFrameAllocator<U8> alloc, VisibilityGroupType type, VisibleNode& x)
-{
-	Group& group = m_groups[type];
-	if(group.m_count + 1 > group.m_nodes.getSize())
-	{
-		// Need to grow
-		U newSize = (group.m_nodes.getSize() != 0) ? group.m_nodes.getSize() * 2 : 2;
-		group.m_nodes.resize(alloc, newSize);
-	}
-
-	group.m_nodes[group.m_count++] = x;
-}
-
-void VisibilityTestResults::combineWith(SceneFrameAllocator<U8> alloc, WeakArray<VisibilityTestResults*>& results)
-{
-	ANKI_ASSERT(results.getSize() > 0);
-
-	// Count the visible scene nodes to optimize the allocation of the
-	// final result
-	Array<U, U(VisibilityGroupType::TYPE_COUNT)> counts;
-	memset(&counts[0], 0, sizeof(counts));
-	for(U i = 0; i < results.getSize(); ++i)
+	U totalElCount = subStorages[0]->m_elementCount;
+	U biggestSubStorageIdx = 0;
+	for(U i = 1; i < subStorages.getSize(); ++i)
 	{
-		VisibilityTestResults& rez = *results[i];
+		totalElCount += subStorages[i]->m_elementCount;
 
-		for(VisibilityGroupType t = VisibilityGroupType::FIRST; t < VisibilityGroupType::TYPE_COUNT; ++t)
+		if(subStorages[i]->m_elementStorage > subStorages[biggestSubStorageIdx]->m_elementStorage)
 		{
-			counts[t] += rez.m_groups[t].m_count;
+			biggestSubStorageIdx = i;
 		}
 	}
 
-	// Allocate
-	for(VisibilityGroupType t = VisibilityGroupType::FIRST; t < VisibilityGroupType::TYPE_COUNT; ++t)
+	if(totalElCount > 0)
 	{
-		if(counts[t] > 0)
+		T* it;
+		if(totalElCount > subStorages[biggestSubStorageIdx]->m_elementStorage)
 		{
-			m_groups[t].m_nodes.create(alloc, counts[t]);
-			m_groups[t].m_count = counts[t];
+			// Can't reuse any storage, will allocate
+
+			it = alloc.newArray<T>(totalElCount);
+			biggestSubStorageIdx = MAX_U;
+
+			combined = WeakArray<T>(it, totalElCount);
 		}
-	}
+		else
+		{
+			// Will reuse existing storage
 
-	// Combine
-	memset(&counts[0], 0, sizeof(counts)); // Re-use
-	for(U i = 0; i < results.getSize(); ++i)
-	{
-		VisibilityTestResults& rez = *results[i];
+			it = subStorages[biggestSubStorageIdx]->m_elements + subStorages[biggestSubStorageIdx]->m_elementCount;
 
-		for(VisibilityGroupType t = VisibilityGroupType::FIRST; t < VisibilityGroupType::TYPE_COUNT; ++t)
+			combined = WeakArray<T>(subStorages[biggestSubStorageIdx]->m_elements, totalElCount);
+		}
+
+		for(U i = 0; i < subStorages.getSize(); ++i)
 		{
-			U copyCount = rez.m_groups[t].m_count;
-			if(copyCount > 0)
+			if(i != biggestSubStorageIdx && subStorages[i]->m_elementCount > 0)
 			{
-				memcpy(
-					&m_groups[t].m_nodes[0] + counts[t], &rez.m_groups[t].m_nodes[0], sizeof(VisibleNode) * copyCount);
-
-				counts[t] += copyCount;
+				memcpy(it, subStorages[i]->m_elements, sizeof(T) * subStorages[i]->m_elementCount);
+				it += subStorages[i]->m_elementCount;
 			}
 		}
 	}
 }
 
-void doVisibilityTests(SceneNode& fsn, SceneGraph& scene)
+void doVisibilityTests(SceneNode& fsn, SceneGraph& scene, RenderQueue& rqueue)
 {
-	ANKI_TRACE_START_EVENT(SCENE_VISIBILITY_TESTS);
+	ANKI_TRACE_SCOPED_EVENT(SCENE_VISIBILITY_TESTS);
 
 	ThreadHive& hive = scene.getThreadHive();
 	scene.getSectorGroup().prepareForVisibilityTests();
 
 	VisibilityContext ctx;
 	ctx.m_scene = &scene;
-	ctx.submitNewWork(fsn.getComponent<FrustumComponent>(), hive);
+	ctx.submitNewWork(fsn.getComponent<FrustumComponent>(), rqueue, hive);
 
 	hive.waitAllTasks();
 	ctx.m_testedFrcs.destroy(scene.getFrameAllocator());
-
-	ANKI_TRACE_STOP_EVENT(SCENE_VISIBILITY_TESTS);
 }
 
 } // end namespace anki

+ 2 - 145
src/anki/scene/Visibility.h

@@ -6,159 +6,16 @@
 #pragma once
 
 #include <anki/scene/Common.h>
-#include <anki/collision/Forward.h>
-#include <anki/scene/SceneNode.h>
-#include <anki/scene/SpatialComponent.h>
-#include <anki/scene/RenderComponent.h>
-#include <anki/util/NonCopyable.h>
+#include <anki/renderer/RenderQueue.h>
 
 namespace anki
 {
 
-// Forward
-class Renderer;
-
 /// @addtogroup scene
 /// @{
 
-/// Visible node pointer with some more info
-/// @note Keep this structure as small as possible
-class VisibleNode
-{
-public:
-	SceneNode* m_node = nullptr;
-	/// An array of the visible spatials
-	U8* m_spatialIndices = nullptr;
-	/// Distance from the frustum component.
-	F32 m_frustumDistance = 0.0;
-	U8 m_spatialsCount = 0;
-
-	VisibleNode()
-	{
-	}
-
-	VisibleNode(const VisibleNode& other)
-	{
-		*this = other;
-	}
-
-	VisibleNode& operator=(const VisibleNode& other)
-	{
-		m_node = other.m_node;
-		m_spatialIndices = other.m_spatialIndices;
-		m_frustumDistance = other.m_frustumDistance;
-		m_spatialsCount = other.m_spatialsCount;
-		return *this;
-	}
-
-	U8 getSpatialIndex(U i) const
-	{
-		ANKI_ASSERT(m_spatialsCount != 0 && i < m_spatialsCount);
-		return m_spatialIndices[i];
-	}
-};
-
-/// The group of nodes that a VisibilityTestResults holds.
-enum class VisibilityGroupType
-{
-	RENDERABLES_MS,
-	RENDERABLES_FS,
-	LIGHTS_SPOT,
-	LIGHTS_POINT,
-	FLARES,
-	REFLECTION_PROBES,
-	REFLECTION_PROXIES,
-	DECALS,
-
-	TYPE_COUNT,
-	FIRST = RENDERABLES_MS
-};
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VisibilityGroupType, inline)
-
-/// It's actually a container for visible entities. It should be per frame.
-class VisibilityTestResults
-{
-public:
-	~VisibilityTestResults()
-	{
-		ANKI_ASSERT(0 && "It's supposed to be deallocated on frame start");
-	}
-
-	void create(SceneFrameAllocator<U8> alloc);
-
-	void prepareMerge();
-
-	VisibleNode* getBegin(VisibilityGroupType type)
-	{
-		return (getCount(type)) ? &m_groups[type].m_nodes[0] : nullptr;
-	}
-
-	VisibleNode* getEnd(VisibilityGroupType type)
-	{
-		return (getCount(type)) ? (&m_groups[type].m_nodes[0] + getCount(type)) : nullptr;
-	}
-
-	const VisibleNode* getBegin(VisibilityGroupType type) const
-	{
-		return (getCount(type)) ? &m_groups[type].m_nodes[0] : nullptr;
-	}
-
-	const VisibleNode* getEnd(VisibilityGroupType type) const
-	{
-		return (getCount(type)) ? (&m_groups[type].m_nodes[0] + getCount(type)) : nullptr;
-	}
-
-	void moveBack(SceneFrameAllocator<U8> alloc, VisibilityGroupType type, VisibleNode& x);
-
-	U32 getCount(VisibilityGroupType type) const
-	{
-		return m_groups[type].m_count;
-	}
-
-	Timestamp getShapeUpdateTimestamp() const
-	{
-		return m_shapeUpdateTimestamp;
-	}
-
-	void setShapeUpdateTimestamp(Timestamp t)
-	{
-		m_shapeUpdateTimestamp = t;
-	}
-
-	void combineWith(SceneFrameAllocator<U8> alloc, WeakArray<VisibilityTestResults*>& results);
-
-	template<typename TFunc>
-	void iterateAll(TFunc f) const
-	{
-		for(VisibilityGroupType i = VisibilityGroupType::FIRST; i < VisibilityGroupType::TYPE_COUNT; ++i)
-		{
-			const VisibleNode* it = getBegin(i);
-			const VisibleNode* end = getEnd(i);
-			while(it != end)
-			{
-				f(*it->m_node);
-				++it;
-			}
-		}
-	}
-
-private:
-	using Container = DynamicArray<VisibleNode>;
-
-	class Group
-	{
-	public:
-		Container m_nodes;
-		U32 m_count = 0;
-	};
-
-	Array<Group, U(VisibilityGroupType::TYPE_COUNT)> m_groups;
-
-	Timestamp m_shapeUpdateTimestamp = 0;
-};
-
 /// Do visibility tests.
-void doVisibilityTests(SceneNode& frustumable, SceneGraph& scene);
+void doVisibilityTests(SceneNode& frustumable, SceneGraph& scene, RenderQueue& rqueue);
 /// @}
 
 } // end namespace anki

+ 53 - 16
src/anki/scene/VisibilityInternal.h

@@ -22,40 +22,71 @@ class ThreadHive;
 /// @addtogroup scene
 /// @{
 
-/// Sort spatial scene nodes on distance
+/// Sort objects on distance
+template<typename T>
 class DistanceSortFunctor
 {
 public:
-	Bool operator()(const VisibleNode& a, const VisibleNode& b)
+	Bool operator()(const T& a, const T& b)
 	{
-		ANKI_ASSERT(a.m_node && b.m_node);
-		return a.m_frustumDistance < b.m_frustumDistance;
+		return a.m_distanceFromCamera < b.m_distanceFromCamera;
 	}
 };
 
+template<typename T>
 class RevDistanceSortFunctor
 {
 public:
-	Bool operator()(const VisibleNode& a, const VisibleNode& b)
+	Bool operator()(const T& a, const T& b)
 	{
-		ANKI_ASSERT(a.m_node && b.m_node);
-		return a.m_frustumDistance > b.m_frustumDistance;
+		return a.m_distanceFromCamera > b.m_distanceFromCamera;
 	}
 };
 
-/// Sort renderable scene nodes on material
-class MaterialSortFunctor
+/// Storage for a single element type.
+template<typename T, U INITIAL_STORAGE_SIZE = 32, U STORAGE_GROW_RATE = 4>
+class TRenderQueueElementStorage
 {
 public:
-	Bool operator()(const VisibleNode& a, const VisibleNode& b)
+	T* m_elements = nullptr;
+	U32 m_elementCount = 0;
+	U32 m_elementStorage = 0;
+
+	Timestamp m_lastUpdateTimestamp;
+
+	T* newElement(SceneFrameAllocator<T> alloc)
 	{
-		ANKI_ASSERT(a.m_node && b.m_node);
+		if(ANKI_UNLIKELY(m_elementCount + 1 > m_elementStorage))
+		{
+			m_elementStorage = max(INITIAL_STORAGE_SIZE, m_elementStorage * STORAGE_GROW_RATE);
+
+			const T* oldElements = m_elements;
+			m_elements = alloc.allocate(m_elementStorage);
 
-		return a.m_node->getComponent<RenderComponent>().getMaterial().getUuid()
-			< b.m_node->getComponent<RenderComponent>().getMaterial().getUuid();
+			if(oldElements)
+			{
+				memcpy(m_elements, oldElements, sizeof(T) * m_elementCount);
+			}
+		}
+
+		return &m_elements[m_elementCount++];
 	}
 };
 
+class RenderQueueView
+{
+public:
+	TRenderQueueElementStorage<RenderableQueueElement> m_renderables; ///< Deferred shading or shadow renderables.
+	TRenderQueueElementStorage<RenderableQueueElement> m_forwardShadingRenderables;
+	TRenderQueueElementStorage<PointLightQueueElement> m_pointLights;
+	TRenderQueueElementStorage<SpotLightQueueElement> m_spotLights;
+	TRenderQueueElementStorage<ReflectionProbeQueueElement> m_reflectionProbes;
+	TRenderQueueElementStorage<LensFlareQueueElement> m_lensFlares;
+	TRenderQueueElementStorage<DecalQueueElement> m_decals;
+};
+
+static_assert(std::is_trivially_destructible<RenderQueueView>::value == true, "Should be trivially destructible");
+
 /// Data common for all tasks.
 class VisibilityContext
 {
@@ -66,7 +97,7 @@ public:
 	List<FrustumComponent*> m_testedFrcs;
 	Mutex m_mtx;
 
-	void submitNewWork(FrustumComponent& frc, ThreadHive& hive);
+	void submitNewWork(FrustumComponent& frc, RenderQueue& result, ThreadHive& hive);
 };
 
 /// ThreadHive task to gather all visible triangles from the OccluderComponent.
@@ -80,7 +111,7 @@ public:
 	DynamicArray<Vec3> m_verts;
 	U32 m_vertCount;
 
-	SoftwareRasterizer m_r;
+	SoftwareRasterizer m_r; // TODO This will never be destroyed
 
 	/// Thread hive task.
 	static void callback(void* ud, U32 threadId, ThreadHive& hive)
@@ -148,7 +179,7 @@ public:
 	WeakPtr<SectorGroupVisibilityTestsContext> m_sectorsCtx;
 	U32 m_taskIdx;
 	U32 m_taskCount;
-	WeakPtr<VisibilityTestResults> m_result;
+	RenderQueueView m_result; ///< Sub result. Will be combined later.
 	Timestamp m_timestamp = 0;
 	SoftwareRasterizer* m_r ANKI_DBG_NULLIFY;
 
@@ -177,6 +208,8 @@ public:
 	WeakPtr<FrustumComponent> m_frc;
 	WeakArray<VisibilityTestTask> m_tests;
 
+	WeakPtr<RenderQueue> m_results; ///< Where to store the results.
+
 	/// Thread hive task.
 	static void callback(void* ud, U32 threadId, ThreadHive& hive)
 	{
@@ -186,6 +219,10 @@ public:
 
 private:
 	void combine();
+
+	template<typename T>
+	void combineQueueElements(
+		SceneFrameAllocator<U8>& alloc, WeakArray<TRenderQueueElementStorage<T>*> subStorages, WeakArray<T>& combined);
 };
 /// @}