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)
{
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)
@@ -161,6 +161,7 @@ const Tileset & Area::getBorder() const
return dBorderCache;
//compute border cache
dBorderCache.reserve(dTiles.bucket_count());
for(const auto & t : dTiles)
{
for(auto & i : int3::getDirs())
@@ -182,6 +183,7 @@ const Tileset & Area::getBorderOutside() const
return dBorderOutsideCache;
//compute outside border cache
dBorderOutsideCache.reserve(dBorderCache.bucket_count() * 2);
for(const auto & t : dTiles)
{
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 subset;
subset.dTiles.reserve(getTilesVector().size());
vstd::copy_if(getTilesVector(), vstd::set_inserter(subset.dTiles), filter);
return subset;
}
@@ -329,7 +332,8 @@ void Area::erase(const int3 & tile)
void Area::unite(const Area & area)
{
invalidate();
auto & vec = area.getTilesVector();
const auto & vec = area.getTilesVector();
dTiles.reserve(dTiles.size() + vec.size());
dTiles.insert(vec.begin(), vec.end());
}
@@ -337,6 +341,7 @@ void Area::intersect(const Area & area)
{
invalidate();
Tileset result;
result.reserve(std::max(dTiles.size(), area.getTilesVector().size()));
for(const auto & t : area.getTilesVector())
{
if(dTiles.count(t))
@@ -361,7 +366,6 @@ void Area::translate(const int3 & shift)
if(dTilesVectorCache.empty())
{
getTiles();
getTilesVector();
}
@@ -396,15 +400,22 @@ Area operator+ (const Area & l, const int3 & 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;
}
Area operator- (const Area & l, const Area & r)
{
Area result(l);
result.subtract(r);
for(const auto & t : r.getTilesVector())
{
result.dTiles.erase(t);
}
return result;
}
@@ -417,7 +428,7 @@ Area 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> 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>;
void toAbsolute(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())
{
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.isBlockedVisitable())
// Do no assume blocked tile is accessible
// Do not assume blocked tile is accessible
dBlockedAreaCache.add(dObject.visitablePos());
}
return dBlockedAreaCache;
@@ -71,7 +72,8 @@ const rmg::Area & Object::Instance::getAccessibleArea() const
{
auto neighbours = rmg::Area({getVisitablePosition()}).getBorderOutside();
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))
dAccessibleAreaCache.add(from);
@@ -277,16 +279,16 @@ const rmg::Area & Object::getAccessibleArea(bool exceptLast) const
return dAccessibleAreaCache;
if(!exceptLast && !dAccessibleAreaFullCache.empty())
return dAccessibleAreaFullCache;
// FIXME: This clears tiles for every consecutive object
for(auto i = dInstances.begin(); i != std::prev(dInstances.end()); ++i)
dAccessibleAreaCache.unite(i->getAccessibleArea());
dAccessibleAreaFullCache = dAccessibleAreaCache;
dAccessibleAreaFullCache.unite(dInstances.back().getAccessibleArea());
dAccessibleAreaCache.subtract(getArea());
dAccessibleAreaFullCache.subtract(getArea());
if(exceptLast)
return dAccessibleAreaCache;
else

View File

@@ -95,7 +95,7 @@ void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> dista
{
RecursiveLock lock(externalAccessMutex);
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);
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
{
for(const auto & tile : searchArea.getTiles())
for(const auto & tile : searchArea.getTilesVector())
{
obj.setPosition(tile);
@@ -469,7 +469,8 @@ bool ObjectManager::createRequiredObjects()
}
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());
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())
{
objectsVisitableArea.add(instance->getVisitablePosition());
@@ -716,7 +718,7 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
return false;
// Prefer non-blocking tiles, if any
const auto & entrableTiles = object.getEntrableArea().getTiles();
const auto & entrableTiles = object.getEntrableArea().getTilesVector();
int3 entrableTile(-1, -1, -1);
if (entrableTiles.empty())
{

View File

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

View File

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

View File

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