Browse Source

Refactoring visibility

Panagiotis Christopoulos Charitos 13 years ago
parent
commit
09b809ac67

+ 3 - 0
include/anki/resource/Mesh.h

@@ -65,6 +65,9 @@ public:
 	{
 		return 1;
 	}
+
+	/// Helper function for correct loading
+	Bool isCompatible(const MeshBase& other) const;
 };
 
 /// Mesh Resource. It contains the geometry packed in VBOs

+ 5 - 0
include/anki/resource/Model.h

@@ -91,6 +91,11 @@ public:
 		for(U32 i = 0; i < meshesCount; i++)
 		{
 			meshes[i].load(meshFNames[i]);
+
+			if(i > 0 && !meshes[i]->isCompatible(*meshes[i - 1]))
+			{
+				throw ANKI_EXCEPTION("Meshes not compatible");
+			}
 		}
 		mtl.load(mtlFName);
 

+ 11 - 13
include/anki/scene/Frustumable.h

@@ -3,7 +3,6 @@
 
 #include "anki/collision/Frustum.h"
 #include "anki/scene/Spatial.h"
-#include "anki/scene/VisibilityTester.h"
 #include "anki/scene/Common.h"
 
 namespace anki {
@@ -19,8 +18,8 @@ struct VisibilityTestResults;
 /// Frustumable interface for scene nodes
 class Frustumable
 {
-	friend SectorGroup;
-	friend Sector;
+	friend class SectorGroup;
+	friend class Sector;
 
 public:
 	/// @name Constructors
@@ -39,15 +38,6 @@ public:
 		return *frustum;
 	}
 
-	const VisibilityInfo& getVisibilityInfo() const
-	{
-		return vinfo;
-	}
-	VisibilityInfo& getVisibilityInfo()
-	{
-		return vinfo;
-	}
-
 	U32 getFrustumableTimestamp() const
 	{
 		return timestamp;
@@ -77,6 +67,15 @@ public:
 
 	/// Get the origin for sorting and visibility tests
 	virtual const Vec3& getFrustumableOrigin() const = 0;
+
+	void setVisibilityTestResults(VisibilityTestResults* visible_)
+	{
+		visible = visible_;
+	}
+	VisibilityTestResults* getVisibilityTestResults()
+	{
+		return visible;
+	}
 	/// @}
 
 	void frustumableMarkUpdated()
@@ -98,7 +97,6 @@ public:
 
 protected:
 	Frustum* frustum = nullptr;
-	VisibilityInfo vinfo;
 	Mat4 projectionMat = Mat4::getIdentity();
 	Mat4 viewMat = Mat4::getIdentity();
 	Mat4 viewProjectionMat = Mat4::getIdentity();

+ 3 - 4
include/anki/scene/Scene.h

@@ -3,7 +3,7 @@
 
 #include "anki/scene/Common.h"
 #include "anki/scene/SceneNode.h"
-#include "anki/scene/VisibilityTester.h"
+#include "anki/scene/Visibility.h"
 #include "anki/scene/Sector.h"
 #include "anki/math/Math.h"
 #include "anki/util/Singleton.h"
@@ -12,7 +12,9 @@
 
 namespace anki {
 
+// Forward
 class Renderer;
+class Camera;
 
 /// @addtogroup Scene
 /// @{
@@ -146,13 +148,10 @@ private:
 	Camera* mainCam = nullptr;
 	U32 activeCameraChangeTimestamp = Timestamp::getTimestamp();
 
-	VisibilityTester vtester;
 	PhysWorld physics;
 
 	SectorGroup sectorGroup;
 
-	void doVisibilityTests(Camera& cam, Renderer& r);
-
 	/// Put a node in the appropriate containers
 	void registerNode(SceneNode* node);
 	void unregisterNode(SceneNode* node);

+ 99 - 3
include/anki/scene/Visibility.h

@@ -3,11 +3,15 @@
 
 #include "anki/scene/Common.h"
 #include "anki/collision/Forward.h"
+#include "anki/scene/SceneNode.h"
+#include "anki/scene/Spatial.h"
+#include "anki/scene/Renderable.h"
+#include "anki/core/ThreadPool.h"
 
 namespace anki {
 
 // Forward
-class SceneNode;
+class Renderer;
 
 /// @addtogroup Scene
 /// @{
@@ -33,14 +37,106 @@ struct VisibilityTestResults
 {
 	typedef SceneFrameVector<SceneNode*> Container;
 
+	/// Used to optimize the initial vector size a bit
+	static const AVERAGE_NUMBER_OF_VISIBLE_SCENE_NODES = 10;
+
 	Container renderables;
 	Container lights;
 
-	VisibilityTestResults(const SceneAllocator<U8>& frameAlloc)
+	VisibilityTestResults(const SceneAllocator<U8>& frameAlloc,
+		U32 renderablesReservedSize = AVERAGE_NUMBER_OF_VISIBLE_SCENE_NODES,
+		U32 lightsReservedSize = AVERAGE_NUMBER_OF_VISIBLE_SCENE_NODES)
 		: renderables(frameAlloc), lights(frameAlloc)
-	{}
+	{
+		renderables.reserve(renderablesReservedSize);
+		lights.reserve(lightsReservedSize);
+	}
+
+	Container::iterator getRenderablesBegin()
+	{
+		return renderables.begin();
+	}
+	Container::iterator getRenderablesEnd()
+	{
+		return renderables.end();
+	}
+	U32 getRenderablesCount() const
+	{
+		return renderables.size();
+	}
+
+	Container::iterator getLightsBegin()
+	{
+		return lights.begin();
+	}
+	Container::iterator getLightsEnd()
+	{
+		return lights.end();
+	}
+};
+
+/// Sort spatial scene nodes on distance
+struct DistanceSortFunctor
+{
+	Vec3 origin;
+
+	Bool operator()(SceneNode* a, SceneNode* b)
+	{
+		ANKI_ASSERT(a->getSpatial() != nullptr && b->getSpatial() != nullptr);
+
+		F32 dist0 = origin.getDistanceSquared(
+			a->getSpatial()->getSpatialOrigin());
+		F32 dist1 = origin.getDistanceSquared(
+			b->getSpatial()->getSpatialOrigin());
+
+		return dist0 < dist1;
+	}
+};
+
+/// Sort renderable scene nodes on material
+struct MaterialSortFunctor
+{
+	Bool operator()(SceneNode* a, SceneNode* b)
+	{
+		ANKI_ASSERT(a->getRenderable() != nullptr 
+			&& b->getRenderable() != nullptr);
+
+		return a->getRenderable()->getRenderableMaterial()
+			< b->getRenderable()->getRenderableMaterial();
+	}
+};
+
+/// Thread job to short scene nodes by distance
+struct DistanceSortJob: ThreadJob
+{
+	U nodesCount;
+	VisibilityTestResults::Container::iterator nodes;
+	Vec3 origin;
+
+	void operator()(U threadId, U threadsCount)
+	{
+		DistanceSortFunctor comp;
+		comp.origin = origin;
+		std::sort(nodes, nodes + nodesCount, comp);
+	}
 };
 
+/// Thread job to short renderable scene nodes by material
+struct MaterialSortJob: ThreadJob
+{
+	U nodesCount;
+	VisibilityTestResults::Container::iterator nodes;
+
+	void operator()(U threadId, U threadsCount)
+	{
+		std::sort(nodes, nodes + nodesCount, MaterialSortFunctor());
+	}
+};
+
+/// Do visibility tests bypassing portals 
+void doVisibilityTests(SceneNode& frustumable, Scene& scene, 
+	Renderer& renderer);
+
 /// @}
 
 } // end namespace anki

+ 0 - 79
include/anki/scene/VisibilityTester.h

@@ -1,79 +0,0 @@
-#ifndef ANKI_SCENE_VISIBILITY_TESTER_H
-#define ANKI_SCENE_VISIBILITY_TESTER_H
-
-#include "anki/util/Vector.h"
-#include "anki/util/StdTypes.h"
-
-namespace anki {
-
-class Camera;
-class Scene;
-class SceneNode;
-class Frustumable;
-class Renderable;
-class Light;
-class Renderer;
-
-/// @addtogroup Scene
-/// @{
-
-/// Its actually a container for visible entities
-class VisibilityInfo
-{
-	friend class VisibilityTester;
-	friend class Octree;
-	friend struct VisibilityTestJob;
-	friend struct DistanceSortJob;
-
-public:
-	typedef Vector<SceneNode*> Renderables;
-	typedef Vector<SceneNode*> Lights;
-
-	Renderables::iterator getRenderablesBegin()
-	{
-		return renderables.begin();
-	}
-	Renderables::iterator getRenderablesEnd()
-	{
-		return renderables.end();
-	}
-	U32 getRenderablesCount() const
-	{
-		return renderables.size();
-	}
-
-	Lights::iterator getLightsBegin()
-	{
-		return lights.begin();
-	}
-	Lights::iterator getLightsEnd()
-	{
-		return lights.end();
-	}
-
-private:
-	Renderables renderables;
-	Lights lights;
-};
-
-/// Performs visibility determination tests and fills a few containers with the
-/// visible scene nodes
-class VisibilityTester
-{
-public:
-	/// Constructor
-	VisibilityTester()
-	{}
-
-	~VisibilityTester();
-
-	/// This method:
-	/// - Gets the visible renderables and frustumables
-	/// - For every frustumable perform tests
-	static void test(Frustumable& ref, Scene& scene, Renderer& r);
-};
-/// @}
-
-} // end namespace anki
-
-#endif

+ 2 - 2
src/renderer/Bs.cpp

@@ -24,8 +24,8 @@ void Bs::run()
 	RenderableDrawer& drawer = r->getSceneDrawer();
 	drawer.prepareDraw();
 	Scene& scene = r->getScene();
-	VisibilityInfo& vi =
-		scene.getActiveCamera().getFrustumable()->getVisibilityInfo();
+	VisibilityTestResults& vi =
+		*scene.getActiveCamera().getFrustumable()->getVisibilityTestResults();
 
 	for(auto it = vi.getRenderablesEnd() - 1; it >= vi.getRenderablesBegin();
 		--it)

+ 2 - 1
src/renderer/Ez.cpp

@@ -27,7 +27,8 @@ void Ez::run()
 	Scene& scene = r->getScene();
 	Camera& cam = scene.getActiveCamera();
 
-	VisibilityInfo& vi = cam.getFrustumable()->getVisibilityInfo();
+	VisibilityTestResults& vi = 
+		*cam.getFrustumable()->getVisibilityTestResults();
 
 	U count = 0;
 	for(auto it = vi.getRenderablesBegin();

+ 2 - 1
src/renderer/Is.cpp

@@ -408,7 +408,8 @@ void Is::initInternal(const RendererInitializer& initializer)
 void Is::lightPass()
 {
 	ThreadPool& threadPool = ThreadPoolSingleton::get();
-	VisibilityInfo& vi = cam->getFrustumable()->getVisibilityInfo();
+	VisibilityTestResults& vi = 
+		*cam->getFrustumable()->getVisibilityTestResults();
 
 	Array<PointLight*, MAX_POINT_LIGHTS> visiblePointLights;
 	U visiblePointLightsCount = 0;

+ 3 - 2
src/renderer/Ms.cpp

@@ -73,8 +73,9 @@ void Ms::run()
 
 	// render all
 	r->getSceneDrawer().prepareDraw();
-	VisibilityInfo& vi =
-		r->getScene().getActiveCamera().getFrustumable()->getVisibilityInfo();
+	VisibilityTestResults& vi =
+		*r->getScene().getActiveCamera().getVisibilityTestResults();
+
 	for(auto it = vi.getRenderablesBegin(); it != vi.getRenderablesEnd(); ++it)
 	{
 		r->getSceneDrawer().render(r->getScene().getActiveCamera(),

+ 1 - 1
src/renderer/Sm.cpp

@@ -154,7 +154,7 @@ Sm::Shadowmap* Sm::doLight(Light& light)
 
 	Frustumable* fr = light.getFrustumable();
 	ANKI_ASSERT(fr != nullptr);
-	VisibilityInfo& vi = fr->getVisibilityInfo();
+	VisibilityTestResults& vi = *fr->getVisibilityTestResults();
 
 	//
 	// Find last update

+ 11 - 0
src/resource/Mesh.cpp

@@ -7,6 +7,17 @@
 
 namespace anki {
 
+//==============================================================================
+// MeshBase                                                                    =
+//==============================================================================
+
+//==============================================================================
+Bool MeshBase::isCompatible(const MeshBase& other) const
+{
+	return hasWeights() == other.hasWeights() 
+		&& getSubMeshesCount() == other.getSubMeshesCount(); 
+}
+
 //==============================================================================
 // Mesh                                                                        =
 //==============================================================================

+ 11 - 10
src/scene/Scene.cpp

@@ -1,7 +1,6 @@
 #include "anki/scene/Scene.h"
 #include "anki/scene/Camera.h"
 #include "anki/util/Exception.h"
-#include "anki/scene/VisibilityTester.h"
 #include "anki/core/ThreadPool.h"
 
 namespace anki {
@@ -103,14 +102,24 @@ void Scene::update(F32 prevUpdateTime, F32 crntTime, Renderer& r)
 	{
 		n->frameUpdate(prevUpdateTime, crntTime, Timestamp::getTimestamp());
 
+		// Do some spatial stuff
 		Spatial* sp = n->getSpatial();
 		if(sp && sp->getSpatialTimestamp() == Timestamp::getTimestamp())
 		{
 			sectorGroup.placeSceneNode(n);
 		}
+
+		sp->disableFlags(Spatial::SF_VISIBLE_ANY);
+
+		// Do some frustumable stuff
+		Frustumable* fr = n->getFrustumable();
+		if(fr)
+		{
+			fr->setVisibilityTestResults(nullptr);
+		}
 	}
 
-	doVisibilityTests(*mainCam, r);
+	doVisibilityTests(*mainCam, *this, r);
 
 	/*sectorGroup.doVisibilityTests(*mainCam,
 		VisibilityTest(VT_RENDERABLES | VT_LIGHTS), &r);*/
@@ -142,14 +151,6 @@ void Scene::update(F32 prevUpdateTime, F32 crntTime, Renderer& r)
 #endif
 }
 
-//==============================================================================
-void Scene::doVisibilityTests(Camera& cam, Renderer& r)
-{
-	Frustumable* f = cam.getFrustumable();
-	ANKI_ASSERT(f != nullptr);
-	vtester.test(*f, *this, r);
-}
-
 //==============================================================================
 SceneNode& Scene::findSceneNode(const char* name)
 {

+ 0 - 62
src/scene/Sector.cpp

@@ -12,68 +12,6 @@
 
 namespace anki {
 
-//==============================================================================
-// Misc                                                                        =
-//==============================================================================
-
-//==============================================================================
-struct DistanceSortFunctor
-{
-	Vec3 origin;
-
-	Bool operator()(SceneNode* a, SceneNode* b)
-	{
-		ANKI_ASSERT(a->getSpatial() != nullptr && b->getSpatial() != nullptr);
-
-		F32 dist0 = origin.getDistanceSquared(
-			a->getSpatial()->getSpatialOrigin());
-		F32 dist1 = origin.getDistanceSquared(
-			b->getSpatial()->getSpatialOrigin());
-
-		return dist0 < dist1;
-	}
-};
-
-//==============================================================================
-struct MaterialSortFunctor
-{
-	Bool operator()(SceneNode* a, SceneNode* b)
-	{
-		ANKI_ASSERT(a->getRenderable() != nullptr
-			&& b->getRenderable() != nullptr);
-
-		return a->getRenderable()->getRenderableMaterial()
-			< b->getRenderable()->getRenderableMaterial();
-	}
-};
-
-//==============================================================================
-struct DistanceSortJob: ThreadJob
-{
-	U nodesCount;
-	VisibilityTestResults::Container::iterator nodes;
-	Vec3 origin;
-
-	void operator()(U threadId, U threadsCount)
-	{
-		DistanceSortFunctor comp;
-		comp.origin = origin;
-		std::sort(nodes, nodes + nodesCount, comp);
-	}
-};
-
-//==============================================================================
-struct MaterialSortJob: ThreadJob
-{
-	U nodesCount;
-	VisibilityTestResults::Container::iterator nodes;
-
-	void operator()(U threadId, U threadsCount)
-	{
-		std::sort(nodes, nodes + nodesCount, MaterialSortFunctor());
-	}
-};
-
 //==============================================================================
 // Portal                                                                      =
 //==============================================================================

+ 211 - 0
src/scene/Visibility.cpp

@@ -0,0 +1,211 @@
+#include "anki/scene/Visibility.h"
+#include "anki/scene/Scene.h"
+#include "anki/scene/Frustumable.h"
+#include "anki/scene/Light.h"
+#include "anki/renderer/Renderer.h"
+
+namespace anki {
+
+//==============================================================================
+struct VisibilityTestJob: ThreadJob
+{
+	U nodesCount = 0;
+	Scene::Types<SceneNode>::Container::iterator nodes;
+	Frustumable* frustumable = nullptr;
+	Renderer* renderer = nullptr;
+	SceneAllocator<U8> frameAlloc;
+
+	VisibilityTestResults* visible;
+
+	/// Do the tests
+	void operator()(U threadId, U threadsCount)
+	{
+		U64 start, end;
+		choseStartEnd(threadId, threadsCount, nodesCount, start, end);
+
+		visible = ANKI_NEW(VisibilityTestResults, frameAlloc, frameAlloc);
+
+		for(auto it = nodes + start; it != nodes + end; it++)
+		{
+			SceneNode* node = *it;
+
+			Frustumable* fr = node->getFrustumable();
+			// Skip if it is the same
+			if(ANKI_UNLIKELY(frustumable == fr))
+			{
+				continue;
+			}
+
+			Spatial* sp = node->getSpatial();
+			if(!sp)
+			{
+				continue;
+			}
+
+			if(!frustumable->insideFrustum(*sp))
+			{
+				continue;
+			}
+
+			Renderable* r = node->getRenderable();
+			if(r)
+			{
+				if(!renderer->doVisibilityTests(
+					sp->getOptimalCollisionShape()))
+				{
+					continue;
+				}
+				visible->renderables.push_back(node);
+			}
+			else
+			{
+				Light* l = node->getLight();
+				if(l)
+				{
+					visible->lights.push_back(node);
+
+					if(l->getShadowEnabled() && fr)
+					{
+						testLight(*l);
+					}
+				}
+			}
+
+			sp->enableFlags(Spatial::SF_VISIBLE_CAMERA);
+		} // end for
+	}
+
+	/// Test an individual light
+	void testLight(Light& light)
+	{
+		Frustumable& ref = *light.getFrustumable();
+		ANKI_ASSERT(&ref != nullptr);
+
+		// Allocate new visibles
+		VisibilityTestResults* lvisible = 
+			ANKI_NEW(VisibilityTestResults, frameAlloc, frameAlloc);
+
+		ref.setVisibilityTestResults(lvisible);
+
+		for(auto it = nodes; it != nodes + nodesCount; it++)
+		{
+			SceneNode* node = *it;
+
+			Frustumable* fr = node->getFrustumable();
+			// Wont check the same
+			if(ANKI_UNLIKELY(&ref == fr))
+			{
+				continue;
+			}
+
+			Spatial* sp = node->getSpatial();
+			if(!sp)
+			{
+				continue;
+			}
+
+			if(!ref.insideFrustum(*sp))
+			{
+				continue;
+			}
+
+			sp->enableFlags(Spatial::SF_VISIBLE_LIGHT);
+
+			Renderable* r = node->getRenderable();
+			if(r)
+			{
+				lvisible->renderables.push_back(node);
+			}
+		}
+	}
+};
+
+//==============================================================================
+void doVisibilityTests(SceneNode& fsn, Scene& scene, 
+	Renderer& r)
+{
+	Frustumable* fr = fsn.getFrustumable();
+	ANKI_ASSERT(fr);
+
+	//
+	// Do the tests in parallel
+	//
+	ThreadPool& threadPool = ThreadPoolSingleton::get();
+	VisibilityTestJob jobs[ThreadPool::MAX_THREADS];
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		jobs[i].nodesCount = scene.getSceneNodesCount();
+		jobs[i].nodes = scene.getSceneNodesBegin();
+		jobs[i].frustumable = fr;
+		jobs[i].renderer = &r;
+		jobs[i].frameAlloc = scene.getFrameAllocator();
+
+		threadPool.assignNewJob(i, &jobs[i]);
+	}
+
+	threadPool.waitForAllJobsToFinish();
+
+	//
+	// Combine results
+	//
+
+	// Count the visible scene nodes to optimize the allocation of the 
+	// final result
+	U32 renderablesSize = 0;
+	U32 lightsSize = 0;
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		renderablesSize += jobs[i].visible->renderables.size();
+		lightsSize += jobs[i].visible->lights.size();
+	}
+
+	// Allocate
+	VisibilityTestResults* visible = 
+		ANKI_NEW(VisibilityTestResults, scene.getFrameAllocator(), 
+		scene.getFrameAllocator(), 
+		renderablesSize, 
+		lightsSize);
+
+	// Append thread results
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		visible->renderables.insert(
+			visible->renderables.end(),
+			jobs[i].visible->renderables.begin(), 
+			jobs[i].visible->renderables.end());
+
+		visible->lights.insert(
+			visible->lights.end(),
+			jobs[i].visible->lights.begin(), 
+			jobs[i].visible->lights.end());
+	}
+
+	// Set the frustumable
+	fr->setVisibilityTestResults(visible);
+
+	//
+	// Sort
+	//
+
+	// The lights
+	DistanceSortJob dsjob;
+	dsjob.nodes = visible->lights.begin();
+	dsjob.nodesCount = visible->lights.size();
+	dsjob.origin = fr->getFrustumableOrigin();
+	threadPool.assignNewJob(0, &dsjob);
+
+	// The rest of the jobs are dummy
+	ThreadJobDummy dummyjobs[ThreadPool::MAX_THREADS];
+	for(U i = 1; i < threadPool.getThreadsCount(); i++)
+	{
+		threadPool.assignNewJob(i, &dummyjobs[i]);
+	}
+
+	// Sort the renderables in the main thread
+	std::sort(visible->renderables.begin(), 
+		visible->renderables.end(), MaterialSortFunctor());
+
+	threadPool.waitForAllJobsToFinish();
+}
+
+} // end namespace anki

+ 0 - 272
src/scene/VisibilityTester.cpp

@@ -1,272 +0,0 @@
-#include "anki/scene/VisibilityTester.h"
-#include "anki/scene/Scene.h"
-#include "anki/scene/Camera.h"
-#include "anki/scene/Renderable.h"
-#include "anki/scene/Light.h"
-#include "anki/renderer/Renderer.h"
-#include "anki/core/ThreadPool.h"
-
-namespace anki {
-
-//==============================================================================
-// Misc                                                                        =
-//==============================================================================
-
-//==============================================================================
-struct DistanceSortFunctor
-{
-	Vec3 origin;
-
-	Bool operator()(SceneNode* a, SceneNode* b)
-	{
-		ANKI_ASSERT(a->getSpatial() != nullptr && b->getSpatial() != nullptr);
-
-		F32 dist0 = origin.getDistanceSquared(
-			a->getSpatial()->getSpatialOrigin());
-		F32 dist1 = origin.getDistanceSquared(
-			b->getSpatial()->getSpatialOrigin());
-
-		return dist0 < dist1;
-	}
-};
-
-//==============================================================================
-struct MaterialSortFunctor
-{
-	Bool operator()(SceneNode* a, SceneNode* b)
-	{
-		ANKI_ASSERT(a->getRenderable() != nullptr 
-			&& b->getRenderable() != nullptr);
-
-		return a->getRenderable()->getRenderableMaterial()
-			< b->getRenderable()->getRenderableMaterial();
-	}
-};
-
-//==============================================================================
-struct VisibilityTestJob: ThreadJob
-{
-	U nodesCount;
-	Scene::Types<SceneNode>::Container::iterator nodes;
-	std::mutex* renderablesMtx;
-	std::mutex* lightsMtx;
-	Frustumable* frustumable;
-	Renderer* renderer;
-
-	void operator()(U threadId, U threadsCount)
-	{
-		U64 start, end;
-		choseStartEnd(threadId, threadsCount, nodesCount, start, end);
-		const U TEMP_STORE_COUNT = 512;
-		Array<SceneNode*, TEMP_STORE_COUNT> tmpRenderables;
-		Array<SceneNode*, TEMP_STORE_COUNT> tmpLights;
-		U renderablesIdx = 0;
-		U lightsIdx = 0;
-
-		for(auto it = nodes + start; it != nodes + end; it++)
-		{
-			SceneNode* node = *it;
-
-			Frustumable* fr = node->getFrustumable();
-			// Skip if it is the same
-			if(frustumable == fr)
-			{
-				continue;
-			}
-
-			Spatial* sp = node->getSpatial();
-			if(!sp)
-			{
-				continue;
-			}
-
-			if(!frustumable->insideFrustum(*sp))
-			{
-				continue;
-			}
-
-			Renderable* r = node->getRenderable();
-			if(r)
-			{
-				if(!renderer->doVisibilityTests(
-					sp->getOptimalCollisionShape()))
-				{
-					continue;
-				}
-				tmpRenderables[renderablesIdx++] = node;
-			}
-			else
-			{
-				Light* l = node->getLight();
-				if(l)
-				{
-					tmpLights[lightsIdx++] = node;
-
-					if(l->getShadowEnabled() && fr)
-					{
-						testLight(*l);
-					}
-				}
-			}
-
-			sp->enableFlags(Spatial::SF_VISIBLE_CAMERA);
-		} // end for
-
-		VisibilityInfo& vinfo = frustumable->getVisibilityInfo();
-
-		// Write to containers
-		if(renderablesIdx > 0)
-		{
-			std::lock_guard<std::mutex> lock(*renderablesMtx);
-
-			vinfo.renderables.insert(vinfo.renderables.begin(),
-				&tmpRenderables[0],
-				&tmpRenderables[renderablesIdx]);
-		}
-
-		if(lightsIdx > 0)
-		{
-			std::lock_guard<std::mutex> lock(*lightsMtx);
-
-			vinfo.lights.insert(vinfo.lights.begin(),
-				&tmpLights[0],
-				&tmpLights[lightsIdx]);
-		}
-	}
-
-	void testLight(Light& light)
-	{
-		Frustumable& ref = *light.getFrustumable();
-		ANKI_ASSERT(&ref != nullptr);
-
-		VisibilityInfo& vinfo = ref.getVisibilityInfo();
-		vinfo.renderables.clear();
-		vinfo.lights.clear();
-
-		for(auto it = nodes; it != nodes + nodesCount; it++)
-		{
-			SceneNode* node = *it;
-
-			Frustumable* fr = node->getFrustumable();
-			// Wont check the same
-			if(&ref == fr)
-			{
-				continue;
-			}
-
-			Spatial* sp = node->getSpatial();
-			if(!sp)
-			{
-				continue;
-			}
-
-			if(!ref.insideFrustum(*sp))
-			{
-				continue;
-			}
-
-			sp->enableFlags(Spatial::SF_VISIBLE_LIGHT);
-
-			Renderable* r = node->getRenderable();
-			if(r)
-			{
-				vinfo.renderables.push_back(node);
-			}
-		}
-	}
-};
-
-//==============================================================================
-struct DistanceSortJob: ThreadJob
-{
-	U nodesCount;
-	VisibilityInfo::Renderables::iterator nodes;
-	Vec3 origin;
-
-	void operator()(U threadId, U threadsCount)
-	{
-		DistanceSortFunctor comp;
-		comp.origin = origin;
-		std::sort(nodes, nodes + nodesCount, comp);
-	}
-};
-
-//==============================================================================
-struct MaterialSortJob: ThreadJob
-{
-	U nodesCount;
-	VisibilityInfo::Renderables::iterator nodes;
-
-	void operator()(U threadId, U threadsCount)
-	{
-		std::sort(nodes, nodes + nodesCount, MaterialSortFunctor());
-	}
-};
-
-//==============================================================================
-VisibilityTester::~VisibilityTester()
-{}
-
-//==============================================================================
-void VisibilityTester::test(Frustumable& ref, Scene& scene, Renderer& r)
-{
-	// Set all spatials to not visible
-	for(auto it = scene.getSceneNodesBegin(); it != scene.getSceneNodesEnd(); ++it)
-	{
-		Spatial* sp = (*it)->getSpatial();
-
-		if(sp)
-		{
-			sp->disableFlags(Spatial::SF_VISIBLE_ANY);
-		}
-	}
-
-	VisibilityInfo& vinfo = ref.getVisibilityInfo();
-	vinfo.renderables.clear();
-	vinfo.lights.clear();
-
-	ThreadPool& threadPool = ThreadPoolSingleton::get();
-	VisibilityTestJob jobs[ThreadPool::MAX_THREADS];
-
-	std::mutex renderablesMtx;
-	std::mutex lightsMtx;
-
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
-	{
-		jobs[i].nodesCount = scene.getSceneNodesCount();
-		jobs[i].nodes = scene.getSceneNodesBegin();
-		jobs[i].renderablesMtx = &renderablesMtx;
-		jobs[i].lightsMtx = &lightsMtx;
-		jobs[i].frustumable = &ref;
-		jobs[i].renderer = &r;
-
-		threadPool.assignNewJob(i, &jobs[i]);
-	}
-
-	threadPool.waitForAllJobsToFinish();
-
-	// Sort
-	//
-
-	// The lights
-	DistanceSortJob dsjob;
-	dsjob.nodes = vinfo.lights.begin();
-	dsjob.nodesCount = vinfo.lights.size();
-	dsjob.origin = ref.getFrustumableOrigin();
-	threadPool.assignNewJob(0, &dsjob);
-
-	// The rest of the jobs are dummy
-	ThreadJobDummy dummyjobs[ThreadPool::MAX_THREADS];
-	for(U i = 1; i < threadPool.getThreadsCount(); i++)
-	{
-		threadPool.assignNewJob(i, &dummyjobs[i]);
-	}
-
-	// Sort the renderables in the main thread
-	std::sort(vinfo.renderables.begin(), 
-		vinfo.renderables.end(), MaterialSortFunctor());
-
-	threadPool.waitForAllJobsToFinish();
-}
-
-} // end namespace anki