From c701d42781f3d6a9029ed74458270be5f5b00950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Mon, 18 Dec 2023 13:52:03 +0100 Subject: [PATCH] Multiple optimizations to avoid copying and allocating tiles for rmg::Area --- lib/rmg/RmgArea.cpp | 33 ++++++++++++++++--------- lib/rmg/RmgArea.h | 2 +- lib/rmg/RmgObject.cpp | 14 ++++++----- lib/rmg/modificators/ObjectManager.cpp | 10 +++++--- lib/rmg/modificators/ObstaclePlacer.cpp | 4 +-- lib/rmg/modificators/TreasurePlacer.cpp | 8 +++--- lib/rmg/modificators/WaterProxy.cpp | 10 ++++---- 7 files changed, 47 insertions(+), 34 deletions(-) diff --git a/lib/rmg/RmgArea.cpp b/lib/rmg/RmgArea.cpp index 690ca5e53..d286e12be 100644 --- a/lib/rmg/RmgArea.cpp +++ b/lib/rmg/RmgArea.cpp @@ -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 & 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(); } } diff --git a/lib/rmg/RmgArea.h b/lib/rmg/RmgArea.h index f07bcd5e0..b04d02809 100644 --- a/lib/rmg/RmgArea.h +++ b/lib/rmg/RmgArea.h @@ -20,7 +20,7 @@ namespace rmg static const std::array dirs4 = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0) }; static const std::array dirsDiagonal= { int3(1,1,0),int3(1,-1,0),int3(-1,1,0),int3(-1,-1,0) }; - using Tileset = std::set; + using Tileset = std::unordered_set; using DistanceMap = std::map; void toAbsolute(Tileset & tiles, const int3 & position); void toRelative(Tileset & tiles, const int3 & position); diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index c8952b0f0..688d46b17 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -38,10 +38,11 @@ const Area & Object::Instance::getBlockedArea() const { if(dBlockedAreaCache.empty()) { - dBlockedAreaCache.assign(dObject.getBlockedPos()); + std::set 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 diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index ccc836a91..23924ff4b 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -95,7 +95,7 @@ void ObjectManager::updateDistances(std::function 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(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 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()) { diff --git a/lib/rmg/modificators/ObstaclePlacer.cpp b/lib/rmg/modificators/ObstaclePlacer.cpp index 26244f8da..8c7e41f87 100644 --- a/lib/rmg/modificators/ObstaclePlacer.cpp +++ b/lib/rmg/modificators/ObstaclePlacer.cpp @@ -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); } diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 79a8c7706..6508fa5cd 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -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); diff --git a/lib/rmg/modificators/WaterProxy.cpp b/lib/rmg/modificators/WaterProxy.cpp index 839511d7c..fae559182 100644 --- a/lib/rmg/modificators/WaterProxy.cpp +++ b/lib/rmg/modificators/WaterProxy.cpp @@ -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);