Browse Source

Refactoring sectors and portals. Fixing a valgrind complaint in pipeline hashing

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
9802fa7c9e

+ 2 - 0
include/anki/gr/GrObjectCache.h

@@ -40,6 +40,7 @@ public:
 	/// Register object in the cache.
 	void registerObject(GrObject* obj)
 	{
+		ANKI_ASSERT(obj && obj->getHash() != 0);
 		ANKI_ASSERT(obj->getHash() != 0);
 		ANKI_ASSERT(tryFind(obj->getHash()) == nullptr);
 		m_map.pushBack(m_alloc, obj->getHash(), obj);
@@ -48,6 +49,7 @@ public:
 	/// Unregister an object from the cache.
 	void unregisterObject(GrObject* obj)
 	{
+		ANKI_ASSERT(obj && obj->getHash() != 0);
 		ANKI_ASSERT(tryFind(obj->getHash()) != nullptr);
 		auto it = m_map.find(obj->getHash());
 		m_map.erase(m_alloc, it);

+ 5 - 5
include/anki/gr/Pipeline.h

@@ -113,11 +113,11 @@ class PipelineInitInfoState
 public:
 	PipelineInitInfoState()
 	{
-// Do a special construction. The state will be hashed and the padding
-// may contain garbage. With this trick zero the padding
-#define ANKI_CONSTRUCT_AND_ZERO_PADDING(member_)                               \
-	memset(&member_, 0, sizeof(member_));                                      \
-	new(&member_) decltype(member_)()
+		// Do a special construction. The state will be hashed and the padding
+		// may contain garbage. With this trick zero the padding
+		memset(this, 0, sizeof(*this));
+
+#define ANKI_CONSTRUCT_AND_ZERO_PADDING(memb_) new(&memb_) decltype(memb_)()
 
 		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_vertex);
 		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_inputAssembler);

+ 5 - 2
include/anki/renderer/DebugDrawer.h

@@ -18,7 +18,8 @@ namespace anki
 
 // Forward
 class Renderer;
-class PortalSectorComponent;
+class PortalComponent;
+class SectorComponent;
 class ReflectionProxyComponent;
 
 /// @addtogroup renderer
@@ -178,7 +179,9 @@ public:
 
 	void draw(SpatialComponent& sp) const;
 
-	void draw(const PortalSectorComponent& c) const;
+	void draw(const PortalComponent& c) const;
+
+	void draw(const SectorComponent& c) const;
 
 	void drawPath(const Path& path) const;
 

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

@@ -28,7 +28,8 @@ enum class SceneComponentType : U16
 	LIGHT,
 	LENS_FLARE,
 	BODY,
-	SECTOR_PORTAL,
+	SECTOR,
+	PORTAL,
 	REFLECTION_PROBE,
 	REFLECTION_PROXY,
 	OCCLUDER,

+ 30 - 13
include/anki/scene/Sector.h

@@ -23,14 +23,25 @@ class SoftwareRasterizer;
 /// @addtogroup scene
 /// @{
 
-/// Dummy component to identify a portal or sector.
-class PortalSectorComponent : public SceneComponent
+/// Dummy component to identify a portal.
+class PortalComponent : public SceneComponent
 {
 public:
-	static const SceneComponentType CLASS_TYPE =
-		SceneComponentType::SECTOR_PORTAL;
+	static const SceneComponentType CLASS_TYPE = SceneComponentType::PORTAL;
 
-	PortalSectorComponent(SceneNode* node)
+	PortalComponent(SceneNode* node)
+		: SceneComponent(CLASS_TYPE, node)
+	{
+	}
+};
+
+/// Dummy component to identify a sector.
+class SectorComponent : public SceneComponent
+{
+public:
+	static const SceneComponentType CLASS_TYPE = SceneComponentType::SECTOR;
+
+	SectorComponent(SceneNode* node)
 		: SceneComponent(CLASS_TYPE, node)
 	{
 	}
@@ -50,7 +61,8 @@ public:
 
 	~PortalSectorBase();
 
-	ANKI_USE_RESULT Error init(const CString& name, const CString& modelFname);
+	ANKI_USE_RESULT Error init(
+		const CString& name, const CString& modelFname, Bool isSector);
 
 	const CollisionShape& getBoundingShape() const
 	{
@@ -75,7 +87,6 @@ protected:
 	CollisionShape* m_shape = nullptr;
 	Aabb m_aabb;
 	DynamicArray<U16> m_vertIndices; ///< Used in debug draw
-	SpinLock m_mtx;
 
 	void updateTransform(const Transform& trf);
 };
@@ -106,9 +117,10 @@ public:
 	/// Remove reference from sector.
 	void tryRemoveSector(Sector* sector);
 
+	void deferredUpdate();
+
 private:
 	List<Sector*> m_sectors;
-	Bool m_open = true;
 };
 
 /// A sector. It consists of an octree and some portals
@@ -138,6 +150,8 @@ public:
 	ANKI_USE_RESULT Error frameUpdate(
 		F32 prevUpdateTime, F32 crntTime) override;
 
+	void deferredUpdate();
+
 private:
 	List<Portal*> m_portals;
 	List<SpatialComponent*> m_spatials;
@@ -176,9 +190,6 @@ private:
 /// Sector group. This is supposed to represent the whole scene
 class SectorGroup
 {
-	friend class Sector;
-	friend class Portal;
-
 public:
 	/// Default constructor
 	SectorGroup(SceneGraph* scene)
@@ -192,6 +203,9 @@ public:
 	void spatialUpdated(SpatialComponent* sp);
 	void spatialDeleted(SpatialComponent* sp);
 
+	void portalUpdated(Portal* portal);
+	void sectorUpdated(Sector* sector);
+
 	void prepareForVisibilityTests();
 
 	void findVisibleNodes(const FrustumComponent& frc,
@@ -201,12 +215,15 @@ public:
 
 private:
 	SceneGraph* m_scene; ///< Keep it here to access various allocators
-	List<Sector*> m_sectors;
-	List<Portal*> m_portals;
 
 	List<SpatialComponent*> m_spatialsDeferredBinning;
 	SpinLock m_mtx;
 
+	List<Portal*> m_portalsUpdated;
+	SpinLock m_portalsUpdatedLock;
+	List<Sector*> m_sectorsUpdated;
+	SpinLock m_sectorsUpdatedLock;
+
 	void findVisibleSectors(const FrustumComponent& frc,
 		const SoftwareRasterizer* r,
 		List<const Sector*>& visibleSectors,

+ 1 - 1
sandbox/Main.cpp

@@ -10,7 +10,7 @@
 
 using namespace anki;
 
-#define PLAYER 1
+#define PLAYER 0
 #define MOUSE 1
 
 class MyApp : public App

+ 1 - 1
sandbox/config.xml

@@ -35,7 +35,7 @@
 	<tm.enabled>1</tm.enabled>
 	<width>1920</width>
 	<height>1088</height>
-	<renderingQuality>0.6</renderingQuality>
+	<renderingQuality>1</renderingQuality>
 	<lodDistance>20</lodDistance>
 	<samples>1</samples>
 	<tessellation>1</tessellation>

+ 8 - 2
src/renderer/Dbg.cpp

@@ -136,8 +136,14 @@ Error Dbg::run(RenderingContext& ctx)
 		// Sector/portal
 		if(m_flags.get(DbgFlag::SECTOR_COMPONENT))
 		{
-			Error err = node.iterateComponentsOfType<PortalSectorComponent>(
-				[&](PortalSectorComponent& psc) -> Error {
+			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;
 				});

+ 30 - 1
src/renderer/DebugDrawer.cpp

@@ -547,7 +547,7 @@ void SceneDebugDrawer::draw(SpatialComponent& x) const
 }
 
 //==============================================================================
-void SceneDebugDrawer::draw(const PortalSectorComponent& c) const
+void SceneDebugDrawer::draw(const SectorComponent& c) const
 {
 	const SceneNode& node = c.getSceneNode();
 	const PortalSectorBase& psnode = static_cast<const PortalSectorBase&>(node);
@@ -575,6 +575,35 @@ void SceneDebugDrawer::draw(const PortalSectorComponent& c) const
 	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::drawPath(const Path& path) const
 {

+ 172 - 144
src/scene/Sector.cpp

@@ -17,6 +17,38 @@
 namespace anki
 {
 
+//==============================================================================
+// Misc                                                                        =
+//==============================================================================
+
+//==============================================================================
+template<typename TFunc>
+static void iterateSceneSectors(SceneGraph& scene, TFunc func)
+{
+	scene.getSceneComponentLists().iterateComponents<SectorComponent>(
+		[&](SectorComponent& comp) {
+			Sector& s = static_cast<Sector&>(comp.getSceneNode());
+			if(func(s))
+			{
+				return;
+			}
+		});
+}
+
+//==============================================================================
+template<typename TFunc>
+static void iterateScenePortals(SceneGraph& scene, TFunc func)
+{
+	scene.getSceneComponentLists().iterateComponents<PortalComponent>(
+		[&](PortalComponent& comp) {
+			Portal& s = static_cast<Portal&>(comp.getSceneNode());
+			if(func(s))
+			{
+				return;
+			}
+		});
+}
+
 //==============================================================================
 // PortalSectorBase                                                            =
 //==============================================================================
@@ -38,7 +70,8 @@ PortalSectorBase::~PortalSectorBase()
 }
 
 //==============================================================================
-Error PortalSectorBase::init(const CString& name, const CString& meshFname)
+Error PortalSectorBase::init(
+	const CString& name, const CString& meshFname, Bool isSector)
 {
 	ANKI_CHECK(SceneNode::init(name));
 
@@ -46,8 +79,15 @@ Error PortalSectorBase::init(const CString& name, const CString& meshFname)
 	SceneComponent* comp = getSceneAllocator().newInstance<MoveComponent>(this);
 	addComponent(comp, true);
 
-	// Create portal sector component
-	comp = getSceneAllocator().newInstance<PortalSectorComponent>(this);
+	// Create portal or sector component
+	if(isSector)
+	{
+		comp = getSceneAllocator().newInstance<SectorComponent>(this);
+	}
+	else
+	{
+		comp = getSceneAllocator().newInstance<PortalComponent>(this);
+	}
 	addComponent(comp, true);
 
 	// Load mesh
@@ -90,12 +130,6 @@ Error PortalSectorBase::init(const CString& name, const CString& meshFname)
 	return ErrorCode::NONE;
 }
 
-//==============================================================================
-SectorGroup& PortalSectorBase::getSectorGroup()
-{
-	return getSceneGraph().getSectorGroup();
-}
-
 //==============================================================================
 void PortalSectorBase::updateTransform(const Transform& trf)
 {
@@ -124,26 +158,12 @@ Portal::~Portal()
 	}
 
 	m_sectors.destroy(getSceneAllocator());
-
-	// Remove from group
-	auto& portals = getSectorGroup().m_portals;
-	auto it = portals.getBegin();
-	auto end = portals.getBegin();
-	for(; it != end; ++it)
-	{
-		if(*it == this)
-		{
-			portals.erase(getSceneAllocator(), it);
-			break;
-		}
-	}
 }
 
 //==============================================================================
 Error Portal::init(const CString& name, const CString& meshFname)
 {
-	ANKI_CHECK(Base::init(name, meshFname));
-	getSectorGroup().m_portals.pushBack(getSceneAllocator(), this);
+	ANKI_CHECK(Base::init(name, meshFname, false));
 	return ErrorCode::NONE;
 }
 
@@ -153,47 +173,47 @@ Error Portal::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 	MoveComponent& move = getComponent<MoveComponent>();
 	if(move.getTimestamp() == getGlobalTimestamp())
 	{
-		// Move comp updated. Inform the group
+		// Move comp updated
 		updateTransform(move.getWorldTransform());
+		getSceneGraph().getSectorGroup().portalUpdated(this);
+	}
 
-		SectorGroup& group = getSectorGroup();
+	return ErrorCode::NONE;
+}
 
-		// Gather the sectors it collides
-		auto it = group.m_sectors.getBegin();
-		auto end = group.m_sectors.getEnd();
+//==============================================================================
+void Portal::deferredUpdate()
+{
+	// Gather the sectors it collides
+	iterateSceneSectors(getSceneGraph(), [&](Sector& sector) -> Bool {
+		Bool collide = testCollisionShapes(m_aabb, sector.m_aabb);
 
-		for(; it != end; ++it)
+		// Perform a more detailed test
+		if(collide)
 		{
-			Sector* sector = *it;
-
-			Bool collide = testCollisionShapes(m_aabb, sector->m_aabb);
-
-			if(collide)
-			{
-				collide =
-					testCollisionShapes(*m_shape, sector->getBoundingShape());
-			}
+			collide = testCollisionShapes(*m_shape, sector.getBoundingShape());
+		}
 
-			if(collide)
-			{
-				tryAddSector(sector);
-				sector->tryAddPortal(this);
-			}
-			else
-			{
-				tryRemoveSector(sector);
-				sector->tryRemovePortal(this);
-			}
+		// Update graphs
+		if(collide)
+		{
+			tryAddSector(&sector);
+			sector.tryAddPortal(this);
+		}
+		else
+		{
+			tryRemoveSector(&sector);
+			sector.tryRemovePortal(this);
 		}
-	}
 
-	return ErrorCode::NONE;
+		return false;
+	});
 }
 
 //==============================================================================
 void Portal::tryAddSector(Sector* sector)
 {
-	LockGuard<SpinLock> lock(m_mtx);
+	ANKI_ASSERT(sector);
 
 	auto it = m_sectors.getBegin();
 	auto end = m_sectors.getEnd();
@@ -212,7 +232,7 @@ void Portal::tryAddSector(Sector* sector)
 //==============================================================================
 void Portal::tryRemoveSector(Sector* sector)
 {
-	LockGuard<SpinLock> lock(m_mtx);
+	ANKI_ASSERT(sector);
 
 	auto it = m_sectors.getBegin();
 	auto end = m_sectors.getEnd();
@@ -249,26 +269,12 @@ Sector::~Sector()
 	{
 		tryRemoveSpatialComponent(m_spatials.getFront());
 	}
-
-	// Remove from group
-	auto& sectors = getSectorGroup().m_sectors;
-	auto it = sectors.getBegin();
-	auto end = sectors.getBegin();
-	for(; it != end; ++it)
-	{
-		if(*it == this)
-		{
-			sectors.erase(getSceneAllocator(), it);
-			break;
-		}
-	}
 }
 
 //==============================================================================
 Error Sector::init(const CString& name, const CString& meshFname)
 {
-	ANKI_CHECK(PortalSectorBase::init(name, meshFname));
-	getSectorGroup().m_sectors.pushBack(getSceneAllocator(), this);
+	ANKI_CHECK(PortalSectorBase::init(name, meshFname, true));
 	return ErrorCode::NONE;
 }
 
@@ -276,7 +282,6 @@ Error Sector::init(const CString& name, const CString& meshFname)
 void Sector::tryAddPortal(Portal* portal)
 {
 	ANKI_ASSERT(portal);
-	LockGuard<SpinLock> lock(m_mtx);
 
 	auto it = m_portals.getBegin();
 	auto end = m_portals.getEnd();
@@ -296,7 +301,6 @@ void Sector::tryAddPortal(Portal* portal)
 void Sector::tryRemovePortal(Portal* portal)
 {
 	ANKI_ASSERT(portal);
-	LockGuard<SpinLock> lock(m_mtx);
 
 	auto it = m_portals.getBegin();
 	auto end = m_portals.getEnd();
@@ -314,7 +318,6 @@ void Sector::tryRemovePortal(Portal* portal)
 void Sector::tryAddSpatialComponent(SpatialComponent* sp)
 {
 	ANKI_ASSERT(sp);
-	LockGuard<SpinLock> lock(m_mtx);
 
 	// Find sector in spatial
 	auto itsp = sp->getSectorInfo().getBegin();
@@ -340,7 +343,6 @@ void Sector::tryAddSpatialComponent(SpatialComponent* sp)
 void Sector::tryRemoveSpatialComponent(SpatialComponent* sp)
 {
 	ANKI_ASSERT(sp);
-	LockGuard<SpinLock> g(m_mtx);
 
 	// Find sector in spatial
 	auto itsp = sp->getSectorInfo().getBegin();
@@ -396,46 +398,43 @@ Error Sector::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 	if(move.getTimestamp() == getGlobalTimestamp())
 	{
 		// Move comp updated.
-
 		updateTransform(move.getWorldTransform());
+		getSceneGraph().getSectorGroup().sectorUpdated(this);
+	}
 
-		// Spatials should get updated
-		for(SpatialComponent* sp : m_spatials)
-		{
-			sp->markForUpdate();
-		}
-
-		// Inform the group
-		SectorGroup& group = getSectorGroup();
+	return ErrorCode::NONE;
+}
 
-		auto it = group.m_portals.getBegin();
-		auto end = group.m_portals.getEnd();
-		for(; it != end; ++it)
-		{
-			Portal* portal = *it;
+//==============================================================================
+void Sector::deferredUpdate()
+{
+	// Spatials should get updated
+	for(SpatialComponent* sp : m_spatials)
+	{
+		sp->markForUpdate();
+	}
 
-			Bool collide = testCollisionShapes(m_aabb, portal->m_aabb);
+	iterateScenePortals(getSceneGraph(), [&](Portal& portal) -> Bool {
+		Bool collide = testCollisionShapes(m_aabb, portal.m_aabb);
 
-			if(collide)
-			{
-				collide =
-					testCollisionShapes(*m_shape, portal->getBoundingShape());
-			}
+		if(collide)
+		{
+			collide = testCollisionShapes(*m_shape, portal.getBoundingShape());
+		}
 
-			if(collide)
-			{
-				portal->tryAddSector(this);
-				tryAddPortal(portal);
-			}
-			else
-			{
-				portal->tryRemoveSector(this);
-				tryRemovePortal(portal);
-			}
+		if(collide)
+		{
+			portal.tryAddSector(this);
+			tryAddPortal(&portal);
+		}
+		else
+		{
+			portal.tryRemoveSector(this);
+			tryRemovePortal(&portal);
 		}
-	}
 
-	return ErrorCode::NONE;
+		return false;
+	});
 }
 
 //==============================================================================
@@ -445,10 +444,6 @@ Error Sector::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 //==============================================================================
 SectorGroup::~SectorGroup()
 {
-	auto alloc = m_scene->getAllocator();
-
-	m_sectors.destroy(alloc);
-	m_portals.destroy(alloc);
 }
 
 //==============================================================================
@@ -459,16 +454,29 @@ void SectorGroup::spatialUpdated(SpatialComponent* sp)
 	m_spatialsDeferredBinning.pushBack(m_scene->getFrameAllocator(), sp);
 }
 
+//==============================================================================
+void SectorGroup::portalUpdated(Portal* portal)
+{
+	ANKI_ASSERT(portal);
+	LockGuard<SpinLock> lock(m_portalsUpdatedLock);
+	m_portalsUpdated.pushBack(m_scene->getFrameAllocator(), portal);
+}
+
+//==============================================================================
+void SectorGroup::sectorUpdated(Sector* sector)
+{
+	ANKI_ASSERT(sector);
+	LockGuard<SpinLock> lock(m_sectorsUpdatedLock);
+	m_sectorsUpdated.pushBack(m_scene->getFrameAllocator(), sector);
+}
+
 //==============================================================================
 void SectorGroup::binSpatial(SpatialComponent* sp)
 {
-	// Iterate all sectors and bin the spatial
-	auto it = m_sectors.getBegin();
-	auto end = m_sectors.getEnd();
-	for(; it != end; ++it)
-	{
-		Sector& sector = *(*it);
+	ANKI_ASSERT(sp);
 
+	// Iterate all sectors and bin the spatial
+	iterateSceneSectors(*m_scene, [&](Sector& sector) -> Bool {
 		Bool collide = false;
 		if(!sp->getSingleSector())
 		{
@@ -515,18 +523,20 @@ void SectorGroup::binSpatial(SpatialComponent* sp)
 		{
 			sector.tryRemoveSpatialComponent(sp);
 		}
-	}
+
+		return false;
+	});
 }
 
 //==============================================================================
 void SectorGroup::spatialDeleted(SpatialComponent* sp)
 {
-	auto it = m_sectors.getBegin();
-	auto end = m_sectors.getEnd();
-	for(; it != end; ++it)
+	auto it = sp->getSectorInfo().getBegin();
+	auto end = sp->getSectorInfo().getEnd();
+	while(it != end)
 	{
-		Sector& sector = *(*it);
-		sector.tryRemoveSpatialComponent(sp);
+		(*it)->tryRemoveSpatialComponent(sp);
+		++it;
 	}
 }
 
@@ -538,44 +548,46 @@ void SectorGroup::findVisibleSectors(const FrustumComponent& frc,
 {
 	// Find the sector the eye is in
 	Sphere eye(frc.getFrustumOrigin(), frc.getFrustum().getNear());
+	Bool eyeInsideASector = false;
+	Sector* sectorEyeIsInside = nullptr;
 
-	auto it = m_sectors.getBegin();
-	auto end = m_sectors.getEnd();
-	for(; it != end; ++it)
-	{
-		Bool collide = testCollisionShapes(eye, (*it)->m_aabb);
+	iterateSceneSectors(*m_scene, [&](Sector& sector) -> Bool {
+		Bool collide = testCollisionShapes(eye, sector.m_aabb);
 
 		if(collide)
 		{
-			collide = testCollisionShapes(eye, (*it)->getBoundingShape());
+			collide = testCollisionShapes(eye, sector.getBoundingShape());
 		}
 
 		if(collide)
 		{
-			break;
+			eyeInsideASector = true;
+			sectorEyeIsInside = &sector;
+			return true;
 		}
-	}
 
-	if(it == end)
+		return false;
+	});
+
+	if(!eyeInsideASector)
 	{
 		// eye outside all sectors, find those the frustum collides
 
-		it = m_sectors.getBegin();
-		for(; it != end; ++it)
-		{
-			const Sector& s = *(*it);
-			if(frc.insideFrustum(s.getBoundingShape()))
+		iterateSceneSectors(*m_scene, [&](Sector& sector) -> Bool {
+			if(frc.insideFrustum(sector.getBoundingShape()))
 			{
 				findVisibleSectorsInternal(
-					frc, s, r, visibleSectors, spatialsCount);
+					frc, sector, r, visibleSectors, spatialsCount);
 			}
-		}
+
+			return false;
+		});
 	}
 	else
 	{
 		// eye inside a sector
 		findVisibleSectorsInternal(
-			frc, *(*it), r, visibleSectors, spatialsCount);
+			frc, *sectorEyeIsInside, r, visibleSectors, spatialsCount);
 	}
 }
 
@@ -632,13 +644,29 @@ void SectorGroup::findVisibleSectorsInternal(const FrustumComponent& frc,
 //==============================================================================
 void SectorGroup::prepareForVisibilityTests()
 {
+	// Update portals
+	Error err = m_portalsUpdated.iterateForward([](Portal* portal) {
+		portal->deferredUpdate();
+		return ErrorCode::NONE;
+	});
+	(void)err;
+	m_portalsUpdated.destroy(m_scene->getFrameAllocator());
+
+	// Update sectors
+	err = m_sectorsUpdated.iterateForward([](Sector* portal) {
+		portal->deferredUpdate();
+		return ErrorCode::NONE;
+	});
+	(void)err;
+	m_sectorsUpdated.destroy(m_scene->getFrameAllocator());
+
 	// Bin spatials
-	auto it = m_spatialsDeferredBinning.getBegin();
-	auto end = m_spatialsDeferredBinning.getEnd();
-	for(; it != end; ++it)
-	{
-		binSpatial(*it);
-	}
+	err =
+		m_spatialsDeferredBinning.iterateForward([this](SpatialComponent* spc) {
+			binSpatial(spc);
+			return ErrorCode::NONE;
+		});
+	(void)err;
 	m_spatialsDeferredBinning.destroy(m_scene->getFrameAllocator());
 }
 
@@ -691,4 +719,4 @@ void SectorGroup::findVisibleNodes(const FrustumComponent& frc,
 	ctx.m_visibleNodes = WeakArray<SceneNode*>(visibleNodesMem, nodesCount);
 }
 
-} // end namespace anki
+} // end namespace anki

+ 46 - 0
tests/util/HashMap.cpp

@@ -9,6 +9,7 @@
 #include "anki/util/DynamicArray.h"
 #include "anki/util/HighRezTimer.h"
 #include <unordered_map>
+#include <algorithm> 
 
 using namespace anki;
 
@@ -104,6 +105,51 @@ ANKI_TEST(Util, HashMap)
 		map.destroy(alloc);
 	}
 
+	// Fuzzy test
+	{
+		const U MAX = 1000;	
+		HashMap<int, int, Hasher, Compare> akMap;
+		std::vector<int> numbers;
+		
+		// Inserd random
+		for(U i = 0; i < MAX; ++i)
+		{
+			U num;
+			while(1)
+			{
+				num = rand();
+				if(std::find(numbers.begin(), numbers.end(), int(num)) 
+					== numbers.end())
+				{
+					// Not found
+					ANKI_TEST_EXPECT_EQ(akMap.find(num), akMap.getEnd());
+					akMap.pushBack(alloc, num, num);
+					numbers.push_back(num);
+					break;
+				}
+				else
+				{
+					// Found
+					ANKI_TEST_EXPECT_NEQ(akMap.find(num), akMap.getEnd());
+				}
+			}
+		}
+		
+		// Remove randomly
+		U count = MAX;
+		while(count--)
+		{
+			U idx = rand() % (count + 1);
+			int num	= numbers[idx];
+			numbers.erase(numbers.begin() + idx);
+			
+			auto it = akMap.find(num);
+			akMap.erase(alloc, it);
+		}
+		
+		akMap.destroy(alloc);
+	}
+
 	// Bench it
 	{
 		HashMap<int, int, Hasher, Compare> akMap;