瀏覽代碼

Visibility work. WONT COMPILE

Panagiotis Christopoulos Charitos 10 年之前
父節點
當前提交
b66cc236f0

+ 1 - 1
include/anki/collision/Frustum.h

@@ -98,7 +98,7 @@ public:
 	void resetTransform(const Transform& trf);
 
 	/// Check if a collision shape @a b is inside the frustum
-	Bool insideFrustum(const CollisionShape& b);
+	Bool insideFrustum(const CollisionShape& b) const;
 
 	/// Calculate the projection matrix
 	virtual Mat4 calculateProjectionMatrix() const = 0;

+ 16 - 3
include/anki/scene/FrustumComponent.h

@@ -116,14 +116,26 @@ public:
 		m_flags |= TRANSFORM_MARKED_FOR_UPDATE;
 	}
 
+	void setShadowCaster(Bool isShadowCaster)
+	{
+		m_flags = (isShadowCaster) 
+			? (m_flags | SHADOW_CASTER)
+			: (m_flags & ~SHADOW_CASTER);
+	}
+
+	Bool getShadowCaster() const
+	{
+		return m_flags & SHADOW_CASTER;
+	}
+
 	/// Is a spatial inside the frustum?
-	Bool insideFrustum(SpatialComponent& sp)
+	Bool insideFrustum(SpatialComponent& sp) const
 	{
 		return m_frustum->insideFrustum(sp.getSpatialCollisionShape());
 	}
 
 	/// Is a collision shape inside the frustum?
-	Bool insideFrustum(const CollisionShape& cs)
+	Bool insideFrustum(const CollisionShape& cs) const
 	{
 		return m_frustum->insideFrustum(cs);
 	}
@@ -142,7 +154,8 @@ private:
 	enum Flags
 	{
 		SHAPE_MARKED_FOR_UPDATE = 1 << 0,
-		TRANSFORM_MARKED_FOR_UPDATE = 1 << 1
+		TRANSFORM_MARKED_FOR_UPDATE = 1 << 1,
+		SHADOW_CASTER = 1 << 2
 	};
 
 	Frustum* m_frustum;

+ 11 - 1
include/anki/scene/SceneComponent.h

@@ -88,8 +88,18 @@ public:
 		return m_flags.bitsEnabled(AUTOMATIC_CLEANUP);
 	}
 
+	SceneNode& getSceneNode()
+	{
+		return *m_node;
+	}
+
+	const SceneNode& getSceneNode() const
+	{
+		return *m_node;
+	}
+
 protected:
-	SceneNode* m_node;
+	SceneNode* m_node = nullptr;
 	Timestamp m_timestamp; ///< Indicates when an update happened
 
 private:

+ 1 - 0
include/anki/scene/SceneGraph.h

@@ -187,6 +187,7 @@ private:
 	const Timestamp* m_globalTimestamp = nullptr;
 	Timestamp m_timestamp = 0; ///< Cached timestamp
 
+	// Sub-systems
 	Threadpool* m_threadpool = nullptr;
 	ResourceManager* m_resources = nullptr;
 	GrManager* m_gr = nullptr;

+ 48 - 10
include/anki/scene/Sector.h

@@ -24,6 +24,8 @@ class FrustumComponent;
 /// 2 way Portal
 class Portal
 {
+	friend class SectorGroup;
+
 public:
 	Portal(SectorGroup* sectorGroup)
 	:	m_group(sectorGroup)
@@ -33,7 +35,7 @@ public:
 
 	ANKI_USE_RESULT Error create(const SArray<Vec4>& vertPositions);
 
-	const CollisionShape& getCollisionShape() const
+	const CollisionShape& getBoundingShape() const
 	{
 		return *m_shape;
 	}
@@ -45,7 +47,7 @@ private:
 	List<Sector*> m_sectors;
 	CollisionShape* m_shape = nullptr;
 	DArray<Vec4> m_shapeStorage;
-	Bool8 m_open = true;
+	Bool m_open = true;
 };
 
 /// A sector. It consists of an octree and some portals
@@ -54,9 +56,6 @@ class Sector
 	friend class SectorGroup;
 
 public:
-	/// Used to reserve some space on the portals vector to save memory
-	static const U AVERAGE_PORTALS_PER_SECTOR = 4;
-
 	/// Default constructor
 	Sector(SectorGroup* group)
 	:	m_group(group)
@@ -66,7 +65,7 @@ public:
 
 	ANKI_USE_RESULT Error create(const SArray<Vec4>& vertPositions);
 
-	const CollisionShape& getCollisionShape() const
+	const CollisionShape& getBoundingShape() const
 	{
 		return *m_shape;
 	}
@@ -118,7 +117,12 @@ public:
 	ANKI_USE_RESULT Error spatialUpdated(SpatialComponent* sp);
 	void spatialDeleted(SpatialComponent* sp);
 
-	ANKI_USE_RESULT Error doVisibilityTests(const FrustumComponent& frc);
+	ANKI_USE_RESULT Error prepareForVisibilityTests(
+		const FrustumComponent& frc);
+
+	template<typename Func>
+	ANKI_USE_RESULT Error iterateVisibleSceneNodes(
+		PtrSize begin, PtrSize end, Func func);
 
 	/// @privatesection
 	/// @{
@@ -132,10 +136,44 @@ private:
 	List<Sector*> m_sectors;
 	List<Portal*> m_portals;
 
-	SceneNode* visibleNodes = nullptr;
-	U visibleNodesCount = 0;
-	U visibleNodesStorage = 0;
+	SceneNode** m_visibleNodes = nullptr;
+	U m_visibleNodesCount = 0;
+
+	ANKI_USE_RESULT Error findVisibleSectors(
+		const FrustumComponent& frc,
+		List<Sector*>& visibleSectors,
+		U& spatialsCount);
+
+	/// Recursive method
+	ANKI_USE_RESULT Error findVisibleSectorsInternal(
+		const FrustumComponent& frc,
+		Sector& s,
+		List<Sector*>& visibleSectors,
+		U& spatialsCount);
 };
+
+//==============================================================================
+template<typename TFunc>
+inline Error SectorGroup::iterateVisibleSceneNodes(
+	PtrSize begin, PtrSize end, TFunc func)
+{
+	Error err = ErrorCode::NONE;
+	SceneNode* prevSn = nullptr;
+
+	SceneNode** it = m_visibleNodes + begin;
+	SceneNode** itend = m_visibleNodes + end;
+	for(; it != itend && !err; ++it)
+	{
+		SceneNode* sn = *it;
+		if(sn != prevSn)
+		{
+			err = func(*sn);
+			prevSn = sn;
+		}
+	}
+
+	return err;
+}
 /// @}
 
 } // end namespace anki

+ 1 - 1
src/collision/Frustum.cpp

@@ -55,7 +55,7 @@ void Frustum::computeAabb(Aabb& aabb) const
 }
 
 //==============================================================================
-Bool Frustum::insideFrustum(const CollisionShape& b)
+Bool Frustum::insideFrustum(const CollisionShape& b) const
 {
 	update();
 

+ 2 - 0
src/scene/Light.cpp

@@ -267,6 +267,8 @@ Error SpotLight::create(const CString& name)
 	FrustumComponent* fr = 
 		getSceneAllocator().newInstance<FrustumComponent>(this, &m_frustum);
 	if(fr == nullptr) return ErrorCode::OUT_OF_MEMORY;
+
+	fr->setShadowCaster(true);
 	
 	err = addComponent(fr, true);
 	if(err) return err;

+ 126 - 10
src/scene/Sector.cpp

@@ -270,7 +270,7 @@ Error SectorGroup::bake()
 			Sector& sector = *(*sit);
 
 			Bool collide = testCollisionShapes(
-				portal.getCollisionShape(), sector.getCollisionShape());
+				portal.getBoundingShape(), sector.getBoundingShape());
 
 			if(collide)
 			{
@@ -296,7 +296,7 @@ Error SectorGroup::spatialUpdated(SpatialComponent* sp)
 		Sector& sector = *(*it);
 
 		Bool collide = testCollisionShapes(
-			sector.getCollisionShape(), sp->getSpatialCollisionShape());
+			sector.getBoundingShape(), sp->getSpatialCollisionShape());
 
 		if(collide)	
 		{
@@ -324,25 +324,141 @@ void SectorGroup::spatialDeleted(SpatialComponent* sp)
 }
 
 //==============================================================================
-Error SectorGroup::doVisibilityTests(const FrustumComponent& frc)
+Error SectorGroup::findVisibleSectors(
+	const FrustumComponent& frc,
+	List<Sector*>& visibleSectors,
+	U& spatialsCount)
 {
-	auto alloc = m_scene->getFrameAllocator();
+	Error err = ErrorCode::NONE;
 
-	// Find the sector the frc is in
+	// Find the sector the eye is in
 	Sphere eye(frc.getFrustumOrigin(), frc.getFrustum().getNear());
 
 	auto it = m_sectors.getBegin();
 	auto end = m_sectors.getEnd();
 	for(; it != end; ++it)
 	{
-		//if(frc.insideFrustum())
+		if(frc.insideFrustum(eye))
+		{
+			break;
+		}
+	}
+
+	if(it == end)
+	{
+		// eye outside all sectors, find those it collides
+
+		it = m_sectors.getBegin();
+		for(; it != end && !err; ++it)
+		{
+			Sector& s = *(*it);
+			if(frc.insideFrustum(s.getBoundingShape()))
+			{
+				err = findVisibleSectorsInternal(
+					frc, s, visibleSectors, spatialsCount);
+			}
+		}
+	}
+	else
+	{
+		// eye inside a sector
+		err = findVisibleSectorsInternal(
+			frc, *(*it), visibleSectors, spatialsCount);
 	}
 
-	// Initial storage
-	visibleNodes = reinterpret_cast<SceneNode*>(
-		alloc.allocate(sizeof(void*) * 100));
-	visibleNodesStorage = 100;
+	return err;
+}
+
+//==============================================================================
+Error SectorGroup::findVisibleSectorsInternal(
+	const FrustumComponent& frc,
+	Sector& s,
+	List<Sector*>& visibleSectors,
+	U& spatialsCount)
+{
+	Error err = ErrorCode::NONE;
+	auto alloc = m_scene->getFrameAllocator();
+
+	// Check if "s" is already there
+	auto it = visibleSectors.getBegin();
+	auto end = visibleSectors.getEnd();
+	for(; it != end; ++it)
+	{
+		if(*it == &s)
+		{
+			// Sector already there, skip
+			return ErrorCode::NONE;
+		}
+	}
 
+	// Sector not in the list, push it
+	ANKI_CHECK(visibleSectors.pushBack(alloc, &s));
+	spatialsCount += s.m_spatials.getSize();
+
+	// Check visible portals
+	auto itp = s.m_portals.getBegin();
+	auto itend = s.m_portals.getEnd();
+	for(; itp != itend && !err; ++itp)
+	{
+		Portal& p = *(*itp);
+		if(frc.insideFrustum(p.getBoundingShape()))
+		{
+			it = p.m_sectors.getBegin();
+			end = p.m_sectors.getEnd();
+			for(; it != end && !err; ++it)
+			{
+				if(*it != &s)
+				{
+					err = findVisibleSectorsInternal(
+						frc, *(*it), visibleSectors, spatialsCount);
+				}
+			}
+		}
+	}
+
+	return err;
+}
+
+//==============================================================================
+Error SectorGroup::prepareForVisibilityTests(const FrustumComponent& frc)
+{
+	auto alloc = m_scene->getFrameAllocator();
+
+	// Find visible sectors
+	List<Sector*> visSectors;
+	U spatialsCount = 0;
+	ANKI_CHECK(findVisibleSectors(frc, visSectors, spatialsCount));
+
+	// Initiate storage of nodes
+	m_visibleNodes = reinterpret_cast<SceneNode**>(
+		alloc.allocate(spatialsCount * sizeof(void*)));
+	SArray<SceneNode*> visibleNodes(m_visibleNodes, spatialsCount);
+
+	// Iterate visible sectors and get the scene nodes. The array will contain
+	// duplicates
+	U nodesCount = 0;
+	for(auto it : visSectors)
+	{
+		Sector& s = *it;
+		for(auto itsp : s.m_spatials)
+		{
+			SpatialComponent& spc = *itsp;
+			SceneNode& sn = spc.getSceneNode();
+
+			visibleNodes[nodesCount++] = &sn;
+		}
+	}
+	m_visibleNodesCount = nodesCount;
+
+	// Sort the scene nodes using the it's address
+	if(nodesCount > 0)
+	{
+		std::sort(
+			visibleNodes.getBegin(), 
+			visibleNodes.getBegin() + nodesCount);
+	}
+
+	return ErrorCode::NONE;
 }
 
 } // end namespace anki

+ 175 - 141
src/scene/Visibility.cpp

@@ -17,73 +17,94 @@ namespace anki {
 // VisibilityTestTask                                                          =
 //==============================================================================
 
-//==============================================================================
+struct ThreadLocal
+{
+	VisibilityTestResults* m_testResults = nullptr;
+};
+
+struct ThreadCommon
+{
+	Array<ThreadLocal, Threadpool::MAX_THREADS> m_threadLocal;
+	Barrier m_barrier;
+	List<SceneNode*> m_frustumsList;
+	SpinLock m_lock;
+}
+
 class VisibilityTestTask: public Threadpool::Task
 {
 public:
-	U m_nodesCount = 0;
 	SceneGraph* m_scene = nullptr;
 	SceneNode* m_frustumableSn = nullptr;
 	SceneFrameAllocator<U8> m_alloc;
 
-	VisibilityTestResults* m_cameraVisible; // out
+	ThreadsLocal* m_threadLocal;
 
 	/// Test a frustum component
-	ANKI_USE_RESULT Error test(SceneNode& testedNode, Bool testingLight, 
+	ANKI_USE_RESULT Error test(SceneNode& testedNode,
 		U32 threadId, PtrSize threadsCount);
 
+	ANKI_USE_RESULT Error combineTestResults(
+		FrustumComponent& frc,
+		PtrSize threadsCount);
+
 	/// Do the tests
 	Error operator()(U32 threadId, PtrSize threadsCount)
 	{
-		return test(*m_frustumableSn, false, threadId, threadsCount);
+		// Run once
+		ANKI_CHECK(test(*m_frustumableSn, threadId, threadsCount));
+
+		// Rucurse the extra frustumables
+		while(!m_frustumsList.isEmpty())
+		{
+			printf("%d: going deeper\n", threadId);
+
+			// Get front
+			SceneNode* node = m_frustumsList.getFront();
+			ANKI_ASSERT(node);
+
+			m_barrier->wait();
+
+			ANKI_CHECK(test(*node, threadId, threadsCount));
+
+			// Pop
+			if(threadId == 0)
+			{
+				m_frustumsList.popFront(m_alloc);
+			}
+		}
+
+		if(threadId == 0)
+		{
+			m_frustumsList.destroy(m_alloc);
+		}
+
+		return ErrorCode::NONE;
 	}
 };
 
 //==============================================================================
-Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight, 
+Error VisibilityTestTask::test(SceneNode& testedNode, 
 	U32 threadId, PtrSize threadsCount)
 {
-	ANKI_ASSERT(testingLight == 
-		(testedNode.tryGetComponent<LightComponent>() != nullptr));
-
 	Error err = ErrorCode::NONE;
 
 	FrustumComponent& testedFr = 
 		testedNode.getComponent<FrustumComponent>();
+	Bool testedNodeShadowCaster = testedFr.getShadowCaster();
 
-	// Allocate visible
+	// Init test results
 	VisibilityTestResults* visible = 
 		m_alloc.newInstance<VisibilityTestResults>();
-	if(visible == nullptr) return ErrorCode::OUT_OF_MEMORY;
 
-	// Init visible
 	FrustumComponent::VisibilityStats stats = testedFr.getLastVisibilityStats();
-	
-	if(!testingLight)
-	{
-		// For camera be conservative
-		stats.m_renderablesCount /= threadsCount;
-		stats.m_lightsCount /= threadsCount;
-	}
 
-	err = visible->create(
-		m_alloc, stats.m_renderablesCount, stats.m_lightsCount, 4);
-	if(err)	return err;
+	ANKI_CHECK(visible->create(
+		m_alloc, stats.m_renderablesCount, stats.m_lightsCount, 4));
 
 	// Chose the test range and a few other things
 	PtrSize start, end;
-	if(!testingLight)
-	{
-		choseStartEnd(threadId, threadsCount, m_nodesCount, start, end);
-		m_cameraVisible = visible;
-	}
-	else
-	{
-		// Is light
-		start = 0;
-		end = m_nodesCount;
-		testedFr.setVisibilityTestResults(visible);
-	}
+	U nodesCount = m_scene->getSceneNodesCount();
+	choseStartEnd(threadId, threadsCount, nodesCount, start, end);
 
 	// Iterate range of nodes
 	err = m_scene->iterateSceneNodes(start, end, [&](SceneNode& node) -> Error
@@ -120,7 +141,7 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 				ANKI_ASSERT(spIdx < MAX_U8);
 				sps[count++] = SpatialTemp{&sp, static_cast<U8>(spIdx)};
 
-				sp.enableBits(testingLight 
+				sp.enableBits(testedNodeShadowCaster 
 					? SpatialComponent::Flag::VISIBLE_LIGHT 
 					: SpatialComponent::Flag::VISIBLE_CAMERA);
 			}
@@ -130,12 +151,12 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 			return ErrorCode::NONE;
 		});
 
-		if(count == 0)
+		if(ANKI_UNLIKELY(count == 0))
 		{
 			return err;
 		}
 
-		// Sort spatials
+		// Sort sub-spatials
 		Vec4 origin = testedFr.getFrustumOrigin();
 		std::sort(sps.begin(), sps.begin() + count, 
 			[origin](const SpatialTemp& a, const SpatialTemp& b) -> Bool
@@ -153,7 +174,7 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 		ANKI_ASSERT(count < MAX_U8);
 		visibleNode.m_spatialsCount = count;
 		visibleNode.m_spatialIndices = m_alloc.newArray<U8>(count);
-		if(visibleNode.m_spatialIndices == nullptr)
+		if(ANKI_UNLIKELY(visibleNode.m_spatialIndices == nullptr))
 		{
 			return ErrorCode::OUT_OF_MEMORY;
 		}
@@ -165,7 +186,7 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 
 		// Do something with the result
 		RenderComponent* r = node.tryGetComponent<RenderComponent>();
-		if(testingLight)
+		if(testedNodeShadowCaster)
 		{
 			if(r && r->getCastsShadow())
 			{
@@ -186,7 +207,8 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 
 				if(!err && l->getShadowEnabled() && fr)
 				{
-					err = test(node, true, 0, 0);
+					LockGuard<SpinLock> l(m_lock);
+					err = m_frustumsList.pushBack(m_alloc, &node);
 				}
 			}
 
@@ -197,114 +219,68 @@ Error VisibilityTestTask::test(SceneNode& testedNode, Bool testingLight,
 				ANKI_ASSERT(visibleNode.m_node);
 			}
 		}
-
+		
 		return err;
 	}); // end for
 
-	return err;
-}
+	ANKI_CHECK(err);
 
-//==============================================================================
-// VisibilityTestResults                                                       =
-//==============================================================================
+	m_threadLocal[threadId].m_testResults = visible;
+	printf("%d: assigning %p\n", threadId, (void*)visible);
 
-//==============================================================================
-Error VisibilityTestResults::create(
-	SceneFrameAllocator<U8> alloc,
-	U32 renderablesReservedSize,
-	U32 lightsReservedSize,
-	U32 lensFlaresReservedSize)
-{
-	Error err = m_renderables.create(alloc, renderablesReservedSize);
-	
-	if(!err)
-	{
-		err = m_lights.create(alloc, lightsReservedSize);
-	}
-
-	if(!err)
-	{
-		err = m_flares.create(alloc, lensFlaresReservedSize);
-	}
-
-	return err;
-}
-
-//==============================================================================
-Error VisibilityTestResults::moveBack(
-	SceneFrameAllocator<U8> alloc, Container& c, U32& count, VisibleNode& x)
-{
-	Error err = ErrorCode::NONE;
+	// Gather the results from all threads
+	m_barrier->wait();
 
-	if(count + 1 > c.getSize())
-	{
-		// Need to grow
-		U newSize = (c.getSize() != 0) ? c.getSize() * 2 : 2;
-		err = c.resize(alloc, newSize);
-	}
+	//printf("%d: checking what is assigned %p\n", threadId, 
+	//		(void*)m_threadLocal[threadId].m_testResults);
 
-	if(!err)
+	printf("%d: checking what is assigned to 0 %p\n", threadId, 
+			(void*)m_threadLocal[0].m_testResults);
+	
+	if(threadId == 0)
 	{
-		c[count++] = x;
+		printf("%d: combine\n", threadId);
+		ANKI_CHECK(combineTestResults(testedFr, threadsCount));
 	}
+	m_barrier->wait();
 
 	return err;
 }
 
 //==============================================================================
-// doVisibilityTests                                                           =
-//==============================================================================
-
-//==============================================================================
-Error doVisibilityTests(SceneNode& fsn, SceneGraph& scene, Renderer& r)
+ANKI_USE_RESULT Error VisibilityTestTask::combineTestResults(
+	FrustumComponent& frc,
+	PtrSize threadsCount)
 {
-	FrustumComponent& fr = fsn.getComponent<FrustumComponent>();
-
-	//
-	// Do the tests in parallel
-	//
-	Threadpool& threadPool = scene._getThreadpool();
-	VisibilityTestTask jobs[Threadpool::MAX_THREADS];
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
-	{
-		jobs[i].m_nodesCount = scene.getSceneNodesCount();
-		jobs[i].m_scene = &scene;
-		jobs[i].m_frustumableSn = &fsn;
-		jobs[i].m_alloc = scene.getFrameAllocator();
-
-		threadPool.assignNewTask(i, &jobs[i]);
-	}
-
-	Error err = threadPool.waitForAllThreadsToFinish();
-	if(err)	return err;
-
-	//
-	// Combine results
-	//
-
 	// Count the visible scene nodes to optimize the allocation of the 
 	// final result
 	U32 renderablesSize = 0;
 	U32 lightsSize = 0;
 	U32 lensFlaresSize = 0;
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	for(U i = 0; i < threadsCount; i++)
 	{
-		renderablesSize += jobs[i].m_cameraVisible->getRenderablesCount();
-		lightsSize += jobs[i].m_cameraVisible->getLightsCount();
-		lensFlaresSize += jobs[i].m_cameraVisible->getLensFlaresCount();
+		ANKI_ASSERT(m_threadLocal[i].m_testResults);
+		VisibilityTestResults& rez = *m_threadLocal[i].m_testResults;
+
+		renderablesSize += rez.getRenderablesCount();
+		lightsSize += rez.getLightsCount();
+		lensFlaresSize += rez.getLensFlaresCount();
 	}
 
 	// Allocate
 	VisibilityTestResults* visible = 
-		scene.getFrameAllocator().newInstance<VisibilityTestResults>();
-	if(visible == nullptr)	return ErrorCode::OUT_OF_MEMORY;
+		m_alloc.newInstance<VisibilityTestResults>();
+	if(visible == nullptr)
+	{
+		return ErrorCode::OUT_OF_MEMORY;
+	}
 
-	err = visible->create(
-		scene.getFrameAllocator(), 
+	ANKI_CHECK(
+		visible->create(
+		m_alloc, 
 		renderablesSize, 
 		lightsSize,
-		lensFlaresSize);
-	if(err)	return err;
+		lensFlaresSize));
 
 	visible->prepareMerge();
 
@@ -317,9 +293,9 @@ Error doVisibilityTests(SceneNode& fsn, SceneGraph& scene, Renderer& r)
 	VisibleNode* renderables = visible->getRenderablesBegin();
 	VisibleNode* lights = visible->getLightsBegin();
 	VisibleNode* lensFlares = visible->getLensFlaresBegin();
-	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	for(U i = 0; i < threadsCount; i++)
 	{
-		VisibilityTestResults& from = *jobs[i].m_cameraVisible;
+		VisibilityTestResults& from = *m_threadLocal[i].m_testResults;
 
 		U rCount = from.getRenderablesCount();
 		U lCount = from.getLightsCount();
@@ -354,34 +330,92 @@ Error doVisibilityTests(SceneNode& fsn, SceneGraph& scene, Renderer& r)
 	}
 
 	// Set the frustumable
-	fr.setVisibilityTestResults(visible);
+	frc.setVisibilityTestResults(visible);
+
+	// Sort lights
+	DistanceSortFunctor comp;
+	comp.m_origin = frc.getFrustumOrigin();
+	//std::sort(visible->getLightsBegin(), visible->getLightsEnd(), comp);
+
+	// Sort the renderables
+	std::sort(
+		visible->getRenderablesBegin(), visible->getRenderablesEnd(), comp);
+
+	return ErrorCode::NONE;
+}
 
-	//
-	// Sort
-	//
+//==============================================================================
+// VisibilityTestResults                                                       =
+//==============================================================================
 
-	// The lights
-	DistanceSortJob dsjob;
-	dsjob.m_nodes = visible->getLightsBegin();
-	dsjob.m_nodesCount = visible->getLightsCount();
-	dsjob.m_origin = fr.getFrustumOrigin();
-	threadPool.assignNewTask(0, &dsjob);
+//==============================================================================
+Error VisibilityTestResults::create(
+	SceneFrameAllocator<U8> alloc,
+	U32 renderablesReservedSize,
+	U32 lightsReservedSize,
+	U32 lensFlaresReservedSize)
+{
+	Error err = m_renderables.create(alloc, renderablesReservedSize);
+	
+	if(!err)
+	{
+		err = m_lights.create(alloc, lightsReservedSize);
+	}
 
-	// The rest of the jobs are dummy
-	for(U i = 1; i < threadPool.getThreadsCount(); i++)
+	if(!err)
 	{
-		threadPool.assignNewTask(i, nullptr);
+		err = m_flares.create(alloc, lensFlaresReservedSize);
 	}
 
-	// Sort the renderables in the main thread
-	DistanceSortFunctor dsfunc;
-	dsfunc.m_origin = fr.getFrustumOrigin();
-	std::sort(
-		visible->getRenderablesBegin(), visible->getRenderablesEnd(), dsfunc);
+	return err;
+}
+
+//==============================================================================
+Error VisibilityTestResults::moveBack(
+	SceneFrameAllocator<U8> alloc, Container& c, U32& count, VisibleNode& x)
+{
+	Error err = ErrorCode::NONE;
+
+	if(count + 1 > c.getSize())
+	{
+		// Need to grow
+		U newSize = (c.getSize() != 0) ? c.getSize() * 2 : 2;
+		err = c.resize(alloc, newSize);
+	}
 
-	err = threadPool.waitForAllThreadsToFinish();
+	if(!err)
+	{
+		c[count++] = x;
+	}
 
 	return err;
 }
 
+//==============================================================================
+// doVisibilityTests                                                           =
+//==============================================================================
+
+//==============================================================================
+Error doVisibilityTests(SceneNode& fsn, SceneGraph& scene, Renderer& r)
+{
+	// Do the tests in parallel
+	Threadpool& threadPool = scene._getThreadpool();
+	Barrier barrier(threadPool.getThreadsCount());
+	ThreadsLocal tlocal;
+
+	Array<VisibilityTestTask, Threadpool::MAX_THREADS> jobs;
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		jobs[i].m_scene = &scene;
+		jobs[i].m_frustumableSn = &fsn;
+		jobs[i].m_alloc = scene.getFrameAllocator();
+		jobs[i].m_barrier = &barrier;
+		jobs[i].m_threadLocal = &tlocal;
+
+		threadPool.assignNewTask(i, &jobs[i]);
+	}
+	
+	return threadPool.waitForAllThreadsToFinish();
+}
+
 } // end namespace anki