Browse Source

Multiple optimizations to avoid copying and allocating tiles for rmg::Area

Tomasz Zieliński 1 year ago
parent
commit
c701d42781

+ 22 - 11
lib/rmg/RmgArea.cpp

@@ -19,12 +19,12 @@ namespace rmg
 
 
 void toAbsolute(Tileset & tiles, const int3 & position)
 void toAbsolute(Tileset & tiles, const int3 & position)
 {
 {
-	Tileset temp;
-	for(const auto & tile : tiles)
+	std::vector vec(tiles.begin(), tiles.end());
+	tiles.clear();
+	std::transform(vec.begin(), vec.end(), vstd::set_inserter(tiles), [position](const int3 & tile)
 	{
 	{
-		temp.insert(tile + position);
-	}
-	tiles = std::move(temp);
+		return tile + position;
+	});
 }
 }
 
 
 void toRelative(Tileset & tiles, const int3 & position)
 void toRelative(Tileset & tiles, const int3 & position)
@@ -161,6 +161,7 @@ const Tileset & Area::getBorder() const
 		return dBorderCache;
 		return dBorderCache;
 	
 	
 	//compute border cache
 	//compute border cache
+	dBorderCache.reserve(dTiles.bucket_count());
 	for(const auto & t : dTiles)
 	for(const auto & t : dTiles)
 	{
 	{
 		for(auto & i : int3::getDirs())
 		for(auto & i : int3::getDirs())
@@ -182,6 +183,7 @@ const Tileset & Area::getBorderOutside() const
 		return dBorderOutsideCache;
 		return dBorderOutsideCache;
 	
 	
 	//compute outside border cache
 	//compute outside border cache
+	dBorderOutsideCache.reserve(dBorderCache.bucket_count() * 2);
 	for(const auto & t : dTiles)
 	for(const auto & t : dTiles)
 	{
 	{
 		for(auto & i : int3::getDirs())
 		for(auto & i : int3::getDirs())
@@ -297,6 +299,7 @@ int3 Area::nearest(const Area & area) const
 Area Area::getSubarea(const std::function<bool(const int3 &)> & filter) const
 Area Area::getSubarea(const std::function<bool(const int3 &)> & filter) const
 {
 {
 	Area subset;
 	Area subset;
+	subset.dTiles.reserve(getTilesVector().size());
 	vstd::copy_if(getTilesVector(), vstd::set_inserter(subset.dTiles), filter);
 	vstd::copy_if(getTilesVector(), vstd::set_inserter(subset.dTiles), filter);
 	return subset;
 	return subset;
 }
 }
@@ -329,7 +332,8 @@ void Area::erase(const int3 & tile)
 void Area::unite(const Area & area)
 void Area::unite(const Area & area)
 {
 {
 	invalidate();
 	invalidate();
-	auto & vec = area.getTilesVector();
+	const auto & vec = area.getTilesVector();
+	dTiles.reserve(dTiles.size() + vec.size());
 	dTiles.insert(vec.begin(), vec.end());
 	dTiles.insert(vec.begin(), vec.end());
 }
 }
 
 
@@ -337,6 +341,7 @@ void Area::intersect(const Area & area)
 {
 {
 	invalidate();
 	invalidate();
 	Tileset result;
 	Tileset result;
+	result.reserve(std::max(dTiles.size(), area.getTilesVector().size()));
 	for(const auto & t : area.getTilesVector())
 	for(const auto & t : area.getTilesVector())
 	{
 	{
 		if(dTiles.count(t))
 		if(dTiles.count(t))
@@ -361,7 +366,6 @@ void Area::translate(const int3 & shift)
 
 
 	if(dTilesVectorCache.empty())
 	if(dTilesVectorCache.empty())
 	{
 	{
-		getTiles();
 		getTilesVector();
 		getTilesVector();
 	}
 	}
 	
 	
@@ -396,15 +400,22 @@ Area operator+ (const Area & l, const int3 & r)
 
 
 Area operator+ (const Area & l, const Area & r)
 Area operator+ (const Area & l, const Area & r)
 {
 {
-	Area result(l);
-	result.unite(r);
+	Area result;
+	const auto & lTiles = l.getTilesVector();
+	const auto & rTiles = r.getTilesVector();
+	result.dTiles.reserve(lTiles.size() + rTiles.size());
+	result.dTiles.insert(lTiles.begin(), lTiles.end());
+	result.dTiles.insert(rTiles.begin(), rTiles.end());
 	return result;
 	return result;
 }
 }
 
 
 Area operator- (const Area & l, const Area & r)
 Area operator- (const Area & l, const Area & r)
 {
 {
 	Area result(l);
 	Area result(l);
-	result.subtract(r);
+	for(const auto & t : r.getTilesVector())
+	{
+		result.dTiles.erase(t);
+	}
 	return result;
 	return result;
 }
 }
 
 
@@ -417,7 +428,7 @@ Area operator* (const Area & l, const Area & r)
 
 
 bool operator== (const Area & l, const Area & r)
 bool operator== (const Area & l, const Area & r)
 {
 {
-	return l.getTiles() == r.getTiles();
+	return l.getTilesVector() == r.getTilesVector();
 }
 }
 
 
 }
 }

+ 1 - 1
lib/rmg/RmgArea.h

@@ -20,7 +20,7 @@ namespace rmg
 	static const std::array<int3, 4> dirs4 = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0) };
 	static const std::array<int3, 4> dirs4 = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0) };
 	static const std::array<int3, 4> dirsDiagonal= { int3(1,1,0),int3(1,-1,0),int3(-1,1,0),int3(-1,-1,0) };
 	static const std::array<int3, 4> dirsDiagonal= { int3(1,1,0),int3(1,-1,0),int3(-1,1,0),int3(-1,-1,0) };
 
 
-	using Tileset = std::set<int3>;
+	using Tileset = std::unordered_set<int3>;
 	using DistanceMap = std::map<int3, int>;
 	using DistanceMap = std::map<int3, int>;
 	void toAbsolute(Tileset & tiles, const int3 & position);
 	void toAbsolute(Tileset & tiles, const int3 & position);
 	void toRelative(Tileset & tiles, const int3 & position);
 	void toRelative(Tileset & tiles, const int3 & position);

+ 8 - 6
lib/rmg/RmgObject.cpp

@@ -38,10 +38,11 @@ const Area & Object::Instance::getBlockedArea() const
 {
 {
 	if(dBlockedAreaCache.empty())
 	if(dBlockedAreaCache.empty())
 	{
 	{
-		dBlockedAreaCache.assign(dObject.getBlockedPos());
+		std::set<int3> blockedArea = dObject.getBlockedPos();
+		dBlockedAreaCache.assign(rmg::Tileset(blockedArea.begin(), blockedArea.end()));
 		if(dObject.isVisitable() || dBlockedAreaCache.empty())
 		if(dObject.isVisitable() || dBlockedAreaCache.empty())
 			if (!dObject.isBlockedVisitable())
 			if (!dObject.isBlockedVisitable())
-				// Do no assume blocked tile is accessible
+				// Do not assume blocked tile is accessible
 				dBlockedAreaCache.add(dObject.visitablePos());
 				dBlockedAreaCache.add(dObject.visitablePos());
 	}
 	}
 	return dBlockedAreaCache;
 	return dBlockedAreaCache;
@@ -71,7 +72,8 @@ const rmg::Area & Object::Instance::getAccessibleArea() const
 	{
 	{
 		auto neighbours = rmg::Area({getVisitablePosition()}).getBorderOutside();
 		auto neighbours = rmg::Area({getVisitablePosition()}).getBorderOutside();
 		rmg::Area visitable = rmg::Area(neighbours) - getBlockedArea();
 		rmg::Area visitable = rmg::Area(neighbours) - getBlockedArea();
-		for(const auto & from : visitable.getTiles())
+		// TODO: Add in one operation to avoid multiple invalidation
+		for(const auto & from : visitable.getTilesVector())
 		{
 		{
 			if(isVisitableFrom(from))
 			if(isVisitableFrom(from))
 				dAccessibleAreaCache.add(from);
 				dAccessibleAreaCache.add(from);
@@ -277,16 +279,16 @@ const rmg::Area & Object::getAccessibleArea(bool exceptLast) const
 		return dAccessibleAreaCache;
 		return dAccessibleAreaCache;
 	if(!exceptLast && !dAccessibleAreaFullCache.empty())
 	if(!exceptLast && !dAccessibleAreaFullCache.empty())
 		return dAccessibleAreaFullCache;
 		return dAccessibleAreaFullCache;
-	
+
 	// FIXME: This clears tiles for every consecutive object
 	// FIXME: This clears tiles for every consecutive object
 	for(auto i = dInstances.begin(); i != std::prev(dInstances.end()); ++i)
 	for(auto i = dInstances.begin(); i != std::prev(dInstances.end()); ++i)
 		dAccessibleAreaCache.unite(i->getAccessibleArea());
 		dAccessibleAreaCache.unite(i->getAccessibleArea());
-	
+
 	dAccessibleAreaFullCache = dAccessibleAreaCache;
 	dAccessibleAreaFullCache = dAccessibleAreaCache;
 	dAccessibleAreaFullCache.unite(dInstances.back().getAccessibleArea());
 	dAccessibleAreaFullCache.unite(dInstances.back().getAccessibleArea());
 	dAccessibleAreaCache.subtract(getArea());
 	dAccessibleAreaCache.subtract(getArea());
 	dAccessibleAreaFullCache.subtract(getArea());
 	dAccessibleAreaFullCache.subtract(getArea());
-	
+
 	if(exceptLast)
 	if(exceptLast)
 		return dAccessibleAreaCache;
 		return dAccessibleAreaCache;
 	else
 	else

+ 6 - 4
lib/rmg/modificators/ObjectManager.cpp

@@ -95,7 +95,7 @@ void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> dista
 {
 {
 	RecursiveLock lock(externalAccessMutex);
 	RecursiveLock lock(externalAccessMutex);
 	tilesByDistance.clear();
 	tilesByDistance.clear();
-	for (const auto & tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
+	for (const auto & tile : zone.areaPossible().getTilesVector()) //don't need to mark distance for not possible tiles
 	{
 	{
 		ui32 d = distanceFunction(tile);
 		ui32 d = distanceFunction(tile);
 		map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
 		map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
@@ -178,7 +178,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
 	}
 	}
 	else
 	else
 	{
 	{
-		for(const auto & tile : searchArea.getTiles())
+		for(const auto & tile : searchArea.getTilesVector())
 		{
 		{
 			obj.setPosition(tile);
 			obj.setPosition(tile);
 
 
@@ -469,7 +469,8 @@ bool ObjectManager::createRequiredObjects()
 		}
 		}
 
 
 		rmg::Object rmgNearObject(*nearby.obj);
 		rmg::Object rmgNearObject(*nearby.obj);
-		rmg::Area possibleArea(rmg::Area(targetObject->getBlockedPos()).getBorderOutside());
+		std::set<int3> blockedArea = targetObject->getBlockedPos();
+		rmg::Area possibleArea(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
 		possibleArea.intersect(zone.areaPossible());
 		possibleArea.intersect(zone.areaPossible());
 		if(possibleArea.empty())
 		if(possibleArea.empty())
 		{
 		{
@@ -587,6 +588,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		}
 		}
 	}
 	}
 	
 	
+	// TODO: Add multiple tiles in one operation to avoid multiple invalidation
 	for(auto * instance : object.instances())
 	for(auto * instance : object.instances())
 	{
 	{
 		objectsVisitableArea.add(instance->getVisitablePosition());
 		objectsVisitableArea.add(instance->getVisitablePosition());
@@ -716,7 +718,7 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
 		return false;
 		return false;
 	
 	
 	// Prefer non-blocking tiles, if any
 	// Prefer non-blocking tiles, if any
-	const auto & entrableTiles = object.getEntrableArea().getTiles();
+	const auto & entrableTiles = object.getEntrableArea().getTilesVector();
 	int3 entrableTile(-1, -1, -1);
 	int3 entrableTile(-1, -1, -1);
 	if (entrableTiles.empty())
 	if (entrableTiles.empty())
 	{
 	{

+ 2 - 2
lib/rmg/modificators/ObstaclePlacer.cpp

@@ -51,7 +51,7 @@ void ObstaclePlacer::process()
 		do
 		do
 		{
 		{
 			toBlock.clear();
 			toBlock.clear();
-			for (const auto& tile : zone.areaPossible().getTiles())
+			for (const auto& tile : zone.areaPossible().getTilesVector())
 			{
 			{
 				rmg::Area neighbors;
 				rmg::Area neighbors;
 				rmg::Area t;
 				rmg::Area t;
@@ -76,7 +76,7 @@ void ObstaclePlacer::process()
 				}
 				}
 			}
 			}
 			zone.areaPossible().subtract(toBlock);
 			zone.areaPossible().subtract(toBlock);
-			for (const auto& tile : toBlock.getTiles())
+			for (const auto& tile : toBlock.getTilesVector())
 			{
 			{
 				map.setOccupied(tile, ETileType::BLOCKED);
 				map.setOccupied(tile, ETileType::BLOCKED);
 			}
 			}

+ 3 - 5
lib/rmg/modificators/TreasurePlacer.cpp

@@ -791,7 +791,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
 	size_t size = 0;
 	size_t size = 0;
 	{
 	{
 		Zone::Lock lock(zone.areaMutex);
 		Zone::Lock lock(zone.areaMutex);
-		size = zone.getArea().getTiles().size();
+		size = zone.getArea().getTilesVector().size();
 	}
 	}
 
 
 	int totalDensity = 0;
 	int totalDensity = 0;
@@ -891,8 +891,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
 						}
 						}
 
 
 						const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
 						const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
-						auto areaToBlock = rmgObject.getAccessibleArea(true);
-						areaToBlock.subtract(guardedArea);
+						const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
 
 
 						if (zone.freePaths().overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
 						if (zone.freePaths().overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
 							return -1.f;
 							return -1.f;
@@ -914,8 +913,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
 				{
 				{
 					guards.unite(rmgObject.instances().back()->getBlockedArea());
 					guards.unite(rmgObject.instances().back()->getBlockedArea());
 					auto guardedArea = rmgObject.instances().back()->getAccessibleArea();
 					auto guardedArea = rmgObject.instances().back()->getAccessibleArea();
-					auto areaToBlock = rmgObject.getAccessibleArea(true);
-					areaToBlock.subtract(guardedArea);
+					auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
 					treasureBlockArea.unite(areaToBlock);
 					treasureBlockArea.unite(areaToBlock);
 				}
 				}
 				zone.connectPath(path);
 				zone.connectPath(path);

+ 5 - 5
lib/rmg/modificators/WaterProxy.cpp

@@ -112,7 +112,7 @@ void WaterProxy::collectLakes()
 		for(const auto & t : lake.getBorderOutside())
 		for(const auto & t : lake.getBorderOutside())
 			if(map.isOnMap(t))
 			if(map.isOnMap(t))
 				lakes.back().neighbourZones[map.getZoneID(t)].add(t);
 				lakes.back().neighbourZones[map.getZoneID(t)].add(t);
-		for(const auto & t : lake.getTiles())
+		for(const auto & t : lake.getTilesVector())
 			lakeMap[t] = lakeId;
 			lakeMap[t] = lakeId;
 		
 		
 		//each lake must have at least one free tile
 		//each lake must have at least one free tile
@@ -143,7 +143,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
 		{
 		{
 			if(!lake.keepConnections.count(dst.getId()))
 			if(!lake.keepConnections.count(dst.getId()))
 			{
 			{
-				for(const auto & ct : lake.neighbourZones[dst.getId()].getTiles())
+				for(const auto & ct : lake.neighbourZones[dst.getId()].getTilesVector())
 				{
 				{
 					if(map.isPossible(ct))
 					if(map.isPossible(ct))
 						map.setOccupied(ct, ETileType::BLOCKED);
 						map.setOccupied(ct, ETileType::BLOCKED);
@@ -155,7 +155,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
 			}
 			}
 
 
 			//Don't place shipyard or boats on the very small lake
 			//Don't place shipyard or boats on the very small lake
-			if (lake.area.getTiles().size() < 25)
+			if (lake.area.getTilesVector().size() < 25)
 			{
 			{
 				logGlobal->info("Skipping very small lake at zone %d", dst.getId());
 				logGlobal->info("Skipping very small lake at zone %d", dst.getId());
 				continue;
 				continue;
@@ -273,7 +273,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout
 
 
 	while(!boardingPositions.empty())
 	while(!boardingPositions.empty())
 	{
 	{
-		auto boardingPosition = *boardingPositions.getTiles().begin();
+		auto boardingPosition = *boardingPositions.getTilesVector().begin();
 		rmg::Area shipPositions({boardingPosition});
 		rmg::Area shipPositions({boardingPosition});
 		auto boutside = shipPositions.getBorderOutside();
 		auto boutside = shipPositions.getBorderOutside();
 		shipPositions.assign(boutside);
 		shipPositions.assign(boutside);
@@ -336,7 +336,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
 	
 	
 	while(!boardingPositions.empty())
 	while(!boardingPositions.empty())
 	{
 	{
-		auto boardingPosition = *boardingPositions.getTiles().begin();
+		auto boardingPosition = *boardingPositions.getTilesVector().begin();
 		rmg::Area shipPositions({boardingPosition});
 		rmg::Area shipPositions({boardingPosition});
 		auto boutside = shipPositions.getBorderOutside();
 		auto boutside = shipPositions.getBorderOutside();
 		shipPositions.assign(boutside);
 		shipPositions.assign(boutside);