1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

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

This commit is contained in:
Tomasz Zieliński
2023-12-18 13:52:03 +01:00
parent 985a2682ae
commit c701d42781
7 changed files with 47 additions and 34 deletions

View File

@@ -19,12 +19,12 @@ namespace rmg
void toAbsolute(Tileset & tiles, const int3 & position) void toAbsolute(Tileset & tiles, const int3 & position)
{ {
Tileset temp; std::vector vec(tiles.begin(), tiles.end());
for(const auto & tile : tiles) tiles.clear();
std::transform(vec.begin(), vec.end(), vstd::set_inserter(tiles), [position](const int3 & tile)
{ {
temp.insert(tile + position); return tile + position;
} });
tiles = std::move(temp);
} }
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); Area result;
result.unite(r); 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();
} }
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -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())
{ {

View File

@@ -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);
} }

View File

@@ -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); const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
areaToBlock.subtract(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); auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
areaToBlock.subtract(guardedArea);
treasureBlockArea.unite(areaToBlock); treasureBlockArea.unite(areaToBlock);
} }
zone.connectPath(path); zone.connectPath(path);

View File

@@ -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);