diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index f89e80e542..894ec6b3b1 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -83,6 +83,9 @@ namespace NavMeshTool ("process-interior-cells", bpo::value<bool>()->implicit_value(true) ->default_value(false), "build navmesh for interior cells") + + ("remove-unused-tiles", bpo::value<bool>()->implicit_value(true) + ->default_value(false), "remove tiles from cache that will not be used with current content profile") ; Files::ConfigurationManager::addCommonOptions(result); @@ -141,6 +144,7 @@ namespace NavMeshTool } const bool processInteriorCells = variables["process-interior-cells"].as<bool>(); + const bool removeUnusedTiles = variables["remove-unused-tiles"].as<bool>(); Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap); @@ -177,7 +181,8 @@ namespace NavMeshTool WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager, esmData, processInteriorCells); - generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber, cellsData, std::move(db)); + generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber, removeUnusedTiles, + cellsData, std::move(db)); Log(Debug::Info) << "Done"; diff --git a/apps/navmeshtool/navmesh.cpp b/apps/navmeshtool/navmesh.cpp index ca614d0cf6..3161192cf9 100644 --- a/apps/navmeshtool/navmesh.cpp +++ b/apps/navmeshtool/navmesh.cpp @@ -24,6 +24,7 @@ #include <utility> #include <vector> #include <random> +#include <string_view> namespace NavMeshTool { @@ -40,6 +41,7 @@ namespace NavMeshTool using DetourNavigator::TileId; using DetourNavigator::TilePosition; using DetourNavigator::TileVersion; + using DetourNavigator::TilesPositionsRange; using Sqlite3::Transaction; void logGeneratedTiles(std::size_t provided, std::size_t expected) @@ -62,8 +64,9 @@ namespace NavMeshTool public: std::atomic_size_t mExpected {0}; - explicit NavMeshTileConsumer(NavMeshDb&& db) + explicit NavMeshTileConsumer(NavMeshDb&& db, bool removeUnusedTiles) : mDb(std::move(db)) + , mRemoveUnusedTiles(removeUnusedTiles) , mTransaction(mDb.startTransaction()) , mNextTileId(mDb.getMaxTileId() + 1) , mNextShapeId(mDb.getMaxShapeId() + 1) @@ -75,13 +78,19 @@ namespace NavMeshTool std::size_t getUpdated() const { return mUpdated.load(); } + std::size_t getDeleted() const + { + const std::lock_guard lock(mMutex); + return mDeleted; + } + std::int64_t resolveMeshSource(const MeshSource& source) override { const std::lock_guard lock(mMutex); return DetourNavigator::resolveMeshSource(mDb, source, mNextShapeId); } - std::optional<NavMeshTileInfo> find(const std::string& worldspace, const TilePosition &tilePosition, + std::optional<NavMeshTileInfo> find(std::string_view worldspace, const TilePosition &tilePosition, const std::vector<std::byte> &input) override { std::optional<NavMeshTileInfo> result; @@ -96,11 +105,34 @@ namespace NavMeshTool return result; } - void ignore() override { report(); } - - void insert(const std::string& worldspace, const TilePosition& tilePosition, std::int64_t version, - const std::vector<std::byte>& input, PreparedNavMeshData& data) override + void ignore(std::string_view worldspace, const TilePosition& tilePosition) override { + if (mRemoveUnusedTiles) + { + std::lock_guard lock(mMutex); + mDeleted += static_cast<std::size_t>(mDb.deleteTilesAt(worldspace, tilePosition)); + } + report(); + } + + void identity(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t tileId) override + { + if (mRemoveUnusedTiles) + { + std::lock_guard lock(mMutex); + mDeleted += static_cast<std::size_t>(mDb.deleteTilesAtExcept(worldspace, tilePosition, TileId {tileId})); + } + report(); + } + + void insert(std::string_view worldspace, const TilePosition& tilePosition, + std::int64_t version, const std::vector<std::byte>& input, PreparedNavMeshData& data) override + { + if (mRemoveUnusedTiles) + { + std::lock_guard lock(mMutex); + mDeleted += static_cast<std::size_t>(mDb.deleteTilesAt(worldspace, tilePosition)); + } data.mUserId = static_cast<unsigned>(mNextTileId); { std::lock_guard lock(mMutex); @@ -111,11 +143,14 @@ namespace NavMeshTool report(); } - void update(std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) override + void update(std::string_view worldspace, const TilePosition& tilePosition, + std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) override { data.mUserId = static_cast<unsigned>(tileId); { std::lock_guard lock(mMutex); + if (mRemoveUnusedTiles) + mDeleted += static_cast<std::size_t>(mDb.deleteTilesAtExcept(worldspace, tilePosition, TileId {tileId})); mDb.updateTile(TileId {tileId}, TileVersion {version}, serialize(data)); } ++mUpdated; @@ -140,49 +175,66 @@ namespace NavMeshTool void commit() { mTransaction.commit(); } + void vacuum() { mDb.vacuum(); } + + void removeTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range) + { + const std::lock_guard lock(mMutex); + mTransaction.commit(); + Log(Debug::Info) << "Removing tiles outside processed range for worldspace \"" << worldspace << "\"..."; + mDeleted += static_cast<std::size_t>(mDb.deleteTilesOutsideRange(worldspace, range)); + mTransaction = mDb.startTransaction(); + } + private: std::atomic_size_t mProvided {0}; std::atomic_size_t mInserted {0}; std::atomic_size_t mUpdated {0}; - std::mutex mMutex; + std::size_t mDeleted = 0; + mutable std::mutex mMutex; NavMeshDb mDb; + const bool mRemoveUnusedTiles; Transaction mTransaction; TileId mNextTileId; std::condition_variable mHasTile; Misc::ProgressReporter<LogGeneratedTiles> mReporter; ShapeId mNextShapeId; + std::mutex mReportMutex; void report() { - mReporter(mProvided + 1, mExpected); - ++mProvided; + const std::size_t provided = mProvided.fetch_add(1, std::memory_order_relaxed) + 1; + mReporter(provided, mExpected); mHasTile.notify_one(); } }; } void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const Settings& settings, - const std::size_t threadsNumber, WorldspaceData& data, NavMeshDb&& db) + std::size_t threadsNumber, bool removeUnusedTiles, WorldspaceData& data, NavMeshDb&& db) { Log(Debug::Info) << "Generating navmesh tiles by " << threadsNumber << " parallel workers..."; SceneUtil::WorkQueue workQueue(threadsNumber); - auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(std::move(db)); + auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(std::move(db), removeUnusedTiles); std::size_t tiles = 0; std::mt19937_64 random; for (const std::unique_ptr<WorldspaceNavMeshInput>& input : data.mNavMeshInputs) { + const auto range = DetourNavigator::makeTilesPositionsRange( + Misc::Convert::toOsgXY(input->mAabb.m_min), + Misc::Convert::toOsgXY(input->mAabb.m_max), + settings.mRecast + ); + + if (removeUnusedTiles) + navMeshTileConsumer->removeTilesOutsideRange(input->mWorldspace, range); + std::vector<TilePosition> worldspaceTiles; - DetourNavigator::getTilesPositions( - DetourNavigator::makeTilesPositionsRange( - Misc::Convert::toOsgXY(input->mAabb.m_min), - Misc::Convert::toOsgXY(input->mAabb.m_max), - settings.mRecast - ), - [&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); } - ); + DetourNavigator::getTilesPositions(range, + [&] (const TilePosition& tilePosition) { worldspaceTiles.push_back(tilePosition); }); tiles += worldspaceTiles.size(); @@ -204,8 +256,19 @@ namespace NavMeshTool navMeshTileConsumer->wait(); navMeshTileConsumer->commit(); + const auto inserted = navMeshTileConsumer->getInserted(); + const auto updated = navMeshTileConsumer->getUpdated(); + const auto deleted = navMeshTileConsumer->getDeleted(); + Log(Debug::Info) << "Generated navmesh for " << navMeshTileConsumer->getProvided() << " tiles, " - << navMeshTileConsumer->getInserted() << " are inserted and " - << navMeshTileConsumer->getUpdated() << " updated"; + << inserted << " are inserted, " + << updated << " updated and " + << deleted << " deleted"; + + if (inserted + updated + deleted > 0) + { + Log(Debug::Info) << "Vacuuming the database..."; + navMeshTileConsumer->vacuum(); + } } } diff --git a/apps/navmeshtool/navmesh.hpp b/apps/navmeshtool/navmesh.hpp index 725f0cd6a4..3d0e9e4665 100644 --- a/apps/navmeshtool/navmesh.hpp +++ b/apps/navmeshtool/navmesh.hpp @@ -4,7 +4,6 @@ #include <osg/Vec3f> #include <cstddef> -#include <string_view> namespace DetourNavigator { @@ -17,7 +16,8 @@ namespace NavMeshTool struct WorldspaceData; void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const DetourNavigator::Settings& settings, - const std::size_t threadsNumber, WorldspaceData& cellsData, DetourNavigator::NavMeshDb&& db); + std::size_t threadsNumber, bool removeUnusedTiles, WorldspaceData& cellsData, + DetourNavigator::NavMeshDb&& db); } #endif diff --git a/apps/openmw_test_suite/detournavigator/navmeshdb.cpp b/apps/openmw_test_suite/detournavigator/navmeshdb.cpp index ba008f50ff..a17f5132c5 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshdb.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshdb.cpp @@ -109,4 +109,61 @@ namespace EXPECT_THROW(mDb.insertTile(tileId, worldspace, tilePosition, version, input, data), std::runtime_error); EXPECT_NO_THROW(insertTile(TileId {54}, version)); } + + TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_at_should_remove_all_tiles_with_given_worldspace_and_position) + { + const TileVersion version {1}; + const std::string worldspace = "sys::default"; + const TilePosition tilePosition {3, 4}; + const std::vector<std::byte> input1 = generateData(); + const std::vector<std::byte> input2 = generateData(); + const std::vector<std::byte> data = generateData(); + ASSERT_EQ(mDb.insertTile(TileId {53}, worldspace, tilePosition, version, input1, data), 1); + ASSERT_EQ(mDb.insertTile(TileId {54}, worldspace, tilePosition, version, input2, data), 1); + ASSERT_EQ(mDb.deleteTilesAt(worldspace, tilePosition), 2); + EXPECT_FALSE(mDb.findTile(worldspace, tilePosition, input1).has_value()); + EXPECT_FALSE(mDb.findTile(worldspace, tilePosition, input2).has_value()); + } + + TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_at_except_should_leave_tile_with_given_id) + { + const TileId leftTileId {53}; + const TileId removedTileId {54}; + const TileVersion version {1}; + const std::string worldspace = "sys::default"; + const TilePosition tilePosition {3, 4}; + const std::vector<std::byte> leftInput = generateData(); + const std::vector<std::byte> removedInput = generateData(); + const std::vector<std::byte> data = generateData(); + ASSERT_EQ(mDb.insertTile(leftTileId, worldspace, tilePosition, version, leftInput, data), 1); + ASSERT_EQ(mDb.insertTile(removedTileId, worldspace, tilePosition, version, removedInput, data), 1); + ASSERT_EQ(mDb.deleteTilesAtExcept(worldspace, tilePosition, leftTileId), 1); + const auto left = mDb.findTile(worldspace, tilePosition, leftInput); + ASSERT_TRUE(left.has_value()); + EXPECT_EQ(left->mTileId, leftTileId); + EXPECT_FALSE(mDb.findTile(worldspace, tilePosition, removedInput).has_value()); + } + + TEST_F(DetourNavigatorNavMeshDbTest, delete_tiles_outside_range_should_leave_tiles_inside_given_rectangle) + { + TileId tileId {1}; + const TileVersion version {1}; + const std::string worldspace = "sys::default"; + const std::vector<std::byte> input = generateData(); + const std::vector<std::byte> data = generateData(); + for (int x = -2; x <= 2; ++x) + { + for (int y = -2; y <= 2; ++y) + { + ASSERT_EQ(mDb.insertTile(tileId, worldspace, TilePosition {x, y}, version, input, data), 1); + ++tileId.t; + } + } + const TilesPositionsRange range {TilePosition {-1, -1}, TilePosition {2, 2}}; + ASSERT_EQ(mDb.deleteTilesOutsideRange(worldspace, range), 16); + for (int x = -2; x <= 2; ++x) + for (int y = -2; y <= 2; ++y) + ASSERT_EQ(mDb.findTile(worldspace, TilePosition {x, y}, input).has_value(), + -1 <= x && x <= 1 && -1 <= y && y <= 1) << "x=" << x << " y=" << y; + } } diff --git a/apps/openmw_test_suite/serialization/format.hpp b/apps/openmw_test_suite/serialization/format.hpp index 8f61838fde..603d2790e0 100644 --- a/apps/openmw_test_suite/serialization/format.hpp +++ b/apps/openmw_test_suite/serialization/format.hpp @@ -5,6 +5,7 @@ #include <utility> #include <type_traits> +#include <cstdint> namespace SerializationTesting { @@ -20,7 +21,7 @@ namespace SerializationTesting } }; - enum Enum + enum Enum : std::int32_t { A, B, @@ -30,7 +31,7 @@ namespace SerializationTesting struct Composite { short mFloatArray[3] = {0}; - std::vector<int> mIntVector; + std::vector<std::int32_t> mIntVector; std::vector<Enum> mEnumVector; std::vector<Pod> mPodVector; std::size_t mPodDataSize = 0; diff --git a/components/detournavigator/generatenavmeshtile.cpp b/components/detournavigator/generatenavmeshtile.cpp index ad8978cd4b..360c05931f 100644 --- a/components/detournavigator/generatenavmeshtile.cpp +++ b/components/detournavigator/generatenavmeshtile.cpp @@ -25,12 +25,14 @@ namespace DetourNavigator { struct Ignore { + std::string_view mWorldspace; + const TilePosition& mTilePosition; std::shared_ptr<NavMeshTileConsumer> mConsumer; ~Ignore() noexcept { if (mConsumer != nullptr) - mConsumer->ignore(); + mConsumer->ignore(mWorldspace, mTilePosition); } }; } @@ -59,7 +61,7 @@ namespace DetourNavigator try { - Ignore ignore {consumer}; + Ignore ignore {mWorldspace, mTilePosition, consumer}; const std::shared_ptr<RecastMesh> recastMesh = mRecastMeshProvider.getMesh(mWorldspace, mTilePosition); @@ -72,7 +74,11 @@ namespace DetourNavigator const std::optional<NavMeshTileInfo> info = consumer->find(mWorldspace, mTilePosition, input); if (info.has_value() && info->mVersion == mSettings.mNavMeshVersion) + { + consumer->identity(mWorldspace, mTilePosition, info->mTileId); + ignore.mConsumer = nullptr; return; + } const auto data = prepareNavMeshTileData(*recastMesh, mTilePosition, mAgentHalfExtents, mSettings.mRecast); @@ -80,7 +86,7 @@ namespace DetourNavigator return; if (info.has_value()) - consumer->update(info->mTileId, mSettings.mNavMeshVersion, *data); + consumer->update(mWorldspace, mTilePosition, info->mTileId, mSettings.mNavMeshVersion, *data); else consumer->insert(mWorldspace, mTilePosition, mSettings.mNavMeshVersion, input, *data); diff --git a/components/detournavigator/generatenavmeshtile.hpp b/components/detournavigator/generatenavmeshtile.hpp index 511b8dfb8f..e6d9e26c1d 100644 --- a/components/detournavigator/generatenavmeshtile.hpp +++ b/components/detournavigator/generatenavmeshtile.hpp @@ -36,15 +36,19 @@ namespace DetourNavigator virtual std::int64_t resolveMeshSource(const MeshSource& source) = 0; - virtual std::optional<NavMeshTileInfo> find(const std::string& worldspace, const TilePosition& tilePosition, + virtual std::optional<NavMeshTileInfo> find(std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) = 0; - virtual void ignore() = 0; + virtual void ignore(std::string_view worldspace, const TilePosition& tilePosition) = 0; - virtual void insert(const std::string& worldspace, const TilePosition& tilePosition, + virtual void identity(std::string_view worldspace, const TilePosition& tilePosition, + std::int64_t tileId) = 0; + + virtual void insert(std::string_view worldspace, const TilePosition& tilePosition, std::int64_t version, const std::vector<std::byte>& input, PreparedNavMeshData& data) = 0; - virtual void update(std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) = 0; + virtual void update(std::string_view worldspace, const TilePosition& tilePosition, + std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) = 0; }; class GenerateNavMeshTile final : public SceneUtil::WorkItem diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index 79188868dc..33c1131176 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -3,6 +3,7 @@ #include "tilebounds.hpp" #include "tileposition.hpp" +#include "tilespositionsrange.hpp" class btVector3; class btTransform; @@ -17,12 +18,6 @@ namespace DetourNavigator { struct RecastSettings; - struct TilesPositionsRange - { - TilePosition mBegin; - TilePosition mEnd; - }; - TilesPositionsRange makeTilesPositionsRange(const osg::Vec2f& aabbMin, const osg::Vec2f& aabbMax, const RecastSettings& settings); diff --git a/components/detournavigator/navmeshdb.cpp b/components/detournavigator/navmeshdb.cpp index ebff250ee0..621c97f390 100644 --- a/components/detournavigator/navmeshdb.cpp +++ b/components/detournavigator/navmeshdb.cpp @@ -10,7 +10,6 @@ #include <sqlite3.h> #include <cstddef> -#include <string> #include <string_view> #include <vector> @@ -35,6 +34,9 @@ namespace DetourNavigator CREATE UNIQUE INDEX IF NOT EXISTS index_unique_tiles_by_worldspace_and_tile_position_and_input ON tiles (worldspace, tile_position_x, tile_position_y, input); + CREATE INDEX IF NOT EXISTS index_tiles_by_worldspace_and_tile_position + ON tiles (worldspace, tile_position_x, tile_position_y); + CREATE TABLE IF NOT EXISTS shapes ( shape_id INTEGER PRIMARY KEY, name TEXT NOT NULL, @@ -83,6 +85,31 @@ namespace DetourNavigator WHERE tile_id = :tile_id )"; + constexpr std::string_view deleteTilesAtQuery = R"( + DELETE FROM tiles + WHERE worldspace = :worldspace + AND tile_position_x = :tile_position_x + AND tile_position_y = :tile_position_y + )"; + + constexpr std::string_view deleteTilesAtExceptQuery = R"( + DELETE FROM tiles + WHERE worldspace = :worldspace + AND tile_position_x = :tile_position_x + AND tile_position_y = :tile_position_y + AND tile_id != :exclude_tile_id + )"; + + constexpr std::string_view deleteTilesOutsideRangeQuery = R"( + DELETE FROM tiles + WHERE worldspace = :worldspace + AND ( tile_position_x < :begin_tile_position_x + OR tile_position_y < :begin_tile_position_y + OR tile_position_x >= :end_tile_position_x + OR tile_position_y >= :end_tile_position_y + ) + )"; + constexpr std::string_view getMaxShapeIdQuery = R"( SELECT max(shape_id) FROM shapes )"; @@ -99,6 +126,10 @@ namespace DetourNavigator INSERT INTO shapes ( shape_id, name, type, hash) VALUES (:shape_id, :name, :type, :hash) )"; + + constexpr std::string_view vacuumQuery = R"( + VACUUM; + )"; } std::ostream& operator<<(std::ostream& stream, ShapeType value) @@ -118,9 +149,13 @@ namespace DetourNavigator , mGetTileData(*mDb, DbQueries::GetTileData {}) , mInsertTile(*mDb, DbQueries::InsertTile {}) , mUpdateTile(*mDb, DbQueries::UpdateTile {}) + , mDeleteTilesAt(*mDb, DbQueries::DeleteTilesAt {}) + , mDeleteTilesAtExcept(*mDb, DbQueries::DeleteTilesAtExcept {}) + , mDeleteTilesOutsideRange(*mDb, DbQueries::DeleteTilesOutsideRange {}) , mGetMaxShapeId(*mDb, DbQueries::GetMaxShapeId {}) , mFindShapeId(*mDb, DbQueries::FindShapeId {}) , mInsertShape(*mDb, DbQueries::InsertShape {}) + , mVacuum(*mDb, DbQueries::Vacuum {}) { } @@ -136,7 +171,7 @@ namespace DetourNavigator return tileId; } - std::optional<Tile> NavMeshDb::findTile(const std::string& worldspace, + std::optional<Tile> NavMeshDb::findTile(std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) { Tile result; @@ -147,7 +182,7 @@ namespace DetourNavigator return result; } - std::optional<TileData> NavMeshDb::getTileData(const std::string& worldspace, + std::optional<TileData> NavMeshDb::getTileData(std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) { TileData result; @@ -159,7 +194,7 @@ namespace DetourNavigator return result; } - int NavMeshDb::insertTile(TileId tileId, const std::string& worldspace, const TilePosition& tilePosition, + int NavMeshDb::insertTile(TileId tileId, std::string_view worldspace, const TilePosition& tilePosition, TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data) { const std::vector<std::byte> compressedInput = Misc::compress(input); @@ -173,6 +208,21 @@ namespace DetourNavigator return execute(*mDb, mUpdateTile, tileId, version, compressedData); } + int NavMeshDb::deleteTilesAt(std::string_view worldspace, const TilePosition& tilePosition) + { + return execute(*mDb, mDeleteTilesAt, worldspace, tilePosition); + } + + int NavMeshDb::deleteTilesAtExcept(std::string_view worldspace, const TilePosition& tilePosition, TileId excludeTileId) + { + return execute(*mDb, mDeleteTilesAtExcept, worldspace, tilePosition, excludeTileId); + } + + int NavMeshDb::deleteTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range) + { + return execute(*mDb, mDeleteTilesOutsideRange, worldspace, range); + } + ShapeId NavMeshDb::getMaxShapeId() { ShapeId shapeId {0}; @@ -180,7 +230,7 @@ namespace DetourNavigator return shapeId; } - std::optional<ShapeId> NavMeshDb::findShapeId(const std::string& name, ShapeType type, + std::optional<ShapeId> NavMeshDb::findShapeId(std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash) { ShapeId shapeId; @@ -189,12 +239,17 @@ namespace DetourNavigator return shapeId; } - int NavMeshDb::insertShape(ShapeId shapeId, const std::string& name, ShapeType type, + int NavMeshDb::insertShape(ShapeId shapeId, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash) { return execute(*mDb, mInsertShape, shapeId, name, type, hash); } + void NavMeshDb::vacuum() + { + execute(*mDb, mVacuum); + } + namespace DbQueries { std::string_view GetMaxTileId::text() noexcept @@ -207,7 +262,7 @@ namespace DetourNavigator return findTileQuery; } - void FindTile::bind(sqlite3& db, sqlite3_stmt& statement, const std::string& worldspace, + void FindTile::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) { Sqlite3::bindParameter(db, statement, ":worldspace", worldspace); @@ -221,7 +276,7 @@ namespace DetourNavigator return getTileDataQuery; } - void GetTileData::bind(sqlite3& db, sqlite3_stmt& statement, const std::string& worldspace, + void GetTileData::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input) { Sqlite3::bindParameter(db, statement, ":worldspace", worldspace); @@ -235,7 +290,7 @@ namespace DetourNavigator return insertTileQuery; } - void InsertTile::bind(sqlite3& db, sqlite3_stmt& statement, TileId tileId, const std::string& worldspace, + void InsertTile::bind(sqlite3& db, sqlite3_stmt& statement, TileId tileId, std::string_view worldspace, const TilePosition& tilePosition, TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data) { @@ -261,6 +316,48 @@ namespace DetourNavigator Sqlite3::bindParameter(db, statement, ":data", data); } + std::string_view DeleteTilesAt::text() noexcept + { + return deleteTilesAtQuery; + } + + void DeleteTilesAt::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, + const TilePosition& tilePosition) + { + Sqlite3::bindParameter(db, statement, ":worldspace", worldspace); + Sqlite3::bindParameter(db, statement, ":tile_position_x", tilePosition.x()); + Sqlite3::bindParameter(db, statement, ":tile_position_y", tilePosition.y()); + } + + std::string_view DeleteTilesAtExcept::text() noexcept + { + return deleteTilesAtExceptQuery; + } + + void DeleteTilesAtExcept::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, + const TilePosition& tilePosition, TileId excludeTileId) + { + Sqlite3::bindParameter(db, statement, ":worldspace", worldspace); + Sqlite3::bindParameter(db, statement, ":tile_position_x", tilePosition.x()); + Sqlite3::bindParameter(db, statement, ":tile_position_y", tilePosition.y()); + Sqlite3::bindParameter(db, statement, ":exclude_tile_id", excludeTileId); + } + + std::string_view DeleteTilesOutsideRange::text() noexcept + { + return deleteTilesOutsideRangeQuery; + } + + void DeleteTilesOutsideRange::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, + const TilesPositionsRange& range) + { + Sqlite3::bindParameter(db, statement, ":worldspace", worldspace); + Sqlite3::bindParameter(db, statement, ":begin_tile_position_x", range.mBegin.x()); + Sqlite3::bindParameter(db, statement, ":begin_tile_position_y", range.mBegin.y()); + Sqlite3::bindParameter(db, statement, ":end_tile_position_x", range.mEnd.x()); + Sqlite3::bindParameter(db, statement, ":end_tile_position_y", range.mEnd.y()); + } + std::string_view GetMaxShapeId::text() noexcept { return getMaxShapeIdQuery; @@ -271,7 +368,7 @@ namespace DetourNavigator return findShapeIdQuery; } - void FindShapeId::bind(sqlite3& db, sqlite3_stmt& statement, const std::string& name, + void FindShapeId::bind(sqlite3& db, sqlite3_stmt& statement, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash) { Sqlite3::bindParameter(db, statement, ":name", name); @@ -284,7 +381,7 @@ namespace DetourNavigator return insertShapeQuery; } - void InsertShape::bind(sqlite3& db, sqlite3_stmt& statement, ShapeId shapeId, const std::string& name, + void InsertShape::bind(sqlite3& db, sqlite3_stmt& statement, ShapeId shapeId, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash) { Sqlite3::bindParameter(db, statement, ":shape_id", shapeId); @@ -292,5 +389,10 @@ namespace DetourNavigator Sqlite3::bindParameter(db, statement, ":type", static_cast<int>(type)); Sqlite3::bindParameter(db, statement, ":hash", hash); } + + std::string_view Vacuum::text() noexcept + { + return vacuumQuery; + } } } diff --git a/components/detournavigator/navmeshdb.hpp b/components/detournavigator/navmeshdb.hpp index 636f1de000..f10a3a3288 100644 --- a/components/detournavigator/navmeshdb.hpp +++ b/components/detournavigator/navmeshdb.hpp @@ -3,6 +3,8 @@ #include "tileposition.hpp" +#include <components/detournavigator/tilespositionsrange.hpp> + #include <components/sqlite3/db.hpp> #include <components/sqlite3/statement.hpp> #include <components/sqlite3/transaction.hpp> @@ -15,7 +17,6 @@ #include <cstring> #include <optional> #include <stdexcept> -#include <string> #include <string_view> #include <tuple> #include <utility> @@ -64,21 +65,21 @@ namespace DetourNavigator struct FindTile { static std::string_view text() noexcept; - static void bind(sqlite3& db, sqlite3_stmt& statement, const std::string& worldspace, + static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input); }; struct GetTileData { static std::string_view text() noexcept; - static void bind(sqlite3& db, sqlite3_stmt& statement, const std::string& worldspace, + static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input); }; struct InsertTile { static std::string_view text() noexcept; - static void bind(sqlite3& db, sqlite3_stmt& statement, TileId tileId, const std::string& worldspace, + static void bind(sqlite3& db, sqlite3_stmt& statement, TileId tileId, std::string_view worldspace, const TilePosition& tilePosition, TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data); }; @@ -90,6 +91,27 @@ namespace DetourNavigator const std::vector<std::byte>& data); }; + struct DeleteTilesAt + { + static std::string_view text() noexcept; + static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, + const TilePosition& tilePosition); + }; + + struct DeleteTilesAtExcept + { + static std::string_view text() noexcept; + static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, + const TilePosition& tilePosition, TileId excludeTileId); + }; + + struct DeleteTilesOutsideRange + { + static std::string_view text() noexcept; + static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view worldspace, + const TilesPositionsRange& range); + }; + struct GetMaxShapeId { static std::string_view text() noexcept; @@ -99,16 +121,22 @@ namespace DetourNavigator struct FindShapeId { static std::string_view text() noexcept; - static void bind(sqlite3& db, sqlite3_stmt& statement, const std::string& name, + static void bind(sqlite3& db, sqlite3_stmt& statement, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash); }; struct InsertShape { static std::string_view text() noexcept; - static void bind(sqlite3& db, sqlite3_stmt& statement, ShapeId shapeId, const std::string& name, + static void bind(sqlite3& db, sqlite3_stmt& statement, ShapeId shapeId, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash); }; + + struct Vacuum + { + static std::string_view text() noexcept; + static void bind(sqlite3&, sqlite3_stmt&) {} + }; } class NavMeshDb @@ -120,22 +148,30 @@ namespace DetourNavigator TileId getMaxTileId(); - std::optional<Tile> findTile(const std::string& worldspace, + std::optional<Tile> findTile(std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input); - std::optional<TileData> getTileData(const std::string& worldspace, + std::optional<TileData> getTileData(std::string_view worldspace, const TilePosition& tilePosition, const std::vector<std::byte>& input); - int insertTile(TileId tileId, const std::string& worldspace, const TilePosition& tilePosition, + int insertTile(TileId tileId, std::string_view worldspace, const TilePosition& tilePosition, TileVersion version, const std::vector<std::byte>& input, const std::vector<std::byte>& data); int updateTile(TileId tileId, TileVersion version, const std::vector<std::byte>& data); + int deleteTilesAt(std::string_view worldspace, const TilePosition& tilePosition); + + int deleteTilesAtExcept(std::string_view worldspace, const TilePosition& tilePosition, TileId excludeTileId); + + int deleteTilesOutsideRange(std::string_view worldspace, const TilesPositionsRange& range); + ShapeId getMaxShapeId(); - std::optional<ShapeId> findShapeId(const std::string& name, ShapeType type, const Sqlite3::ConstBlob& hash); + std::optional<ShapeId> findShapeId(std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash); - int insertShape(ShapeId shapeId, const std::string& name, ShapeType type, const Sqlite3::ConstBlob& hash); + int insertShape(ShapeId shapeId, std::string_view name, ShapeType type, const Sqlite3::ConstBlob& hash); + + void vacuum(); private: Sqlite3::Db mDb; @@ -144,9 +180,13 @@ namespace DetourNavigator Sqlite3::Statement<DbQueries::GetTileData> mGetTileData; Sqlite3::Statement<DbQueries::InsertTile> mInsertTile; Sqlite3::Statement<DbQueries::UpdateTile> mUpdateTile; + Sqlite3::Statement<DbQueries::DeleteTilesAt> mDeleteTilesAt; + Sqlite3::Statement<DbQueries::DeleteTilesAtExcept> mDeleteTilesAtExcept; + Sqlite3::Statement<DbQueries::DeleteTilesOutsideRange> mDeleteTilesOutsideRange; Sqlite3::Statement<DbQueries::GetMaxShapeId> mGetMaxShapeId; Sqlite3::Statement<DbQueries::FindShapeId> mFindShapeId; Sqlite3::Statement<DbQueries::InsertShape> mInsertShape; + Sqlite3::Statement<DbQueries::Vacuum> mVacuum; }; } diff --git a/components/detournavigator/navmeshdbutils.cpp b/components/detournavigator/navmeshdbutils.cpp index 86f81bfc51..71873972b9 100644 --- a/components/detournavigator/navmeshdbutils.cpp +++ b/components/detournavigator/navmeshdbutils.cpp @@ -6,19 +6,20 @@ #include <cassert> #include <optional> +#include <string_view> namespace DetourNavigator { namespace { - std::optional<ShapeId> findShapeId(NavMeshDb& db, const std::string& name, ShapeType type, + std::optional<ShapeId> findShapeId(NavMeshDb& db, std::string_view name, ShapeType type, const std::string& hash) { const Sqlite3::ConstBlob hashData {hash.data(), static_cast<int>(hash.size())}; return db.findShapeId(name, type, hashData); } - ShapeId getShapeId(NavMeshDb& db, const std::string& name, ShapeType type, + ShapeId getShapeId(NavMeshDb& db, std::string_view name, ShapeType type, const std::string& hash, ShapeId& nextShapeId) { const Sqlite3::ConstBlob hashData {hash.data(), static_cast<int>(hash.size())}; diff --git a/components/detournavigator/tilespositionsrange.hpp b/components/detournavigator/tilespositionsrange.hpp new file mode 100644 index 0000000000..d5f2622ba1 --- /dev/null +++ b/components/detournavigator/tilespositionsrange.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILESPOSITIONSRANGE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILESPOSITIONSRANGE_H + +#include "tileposition.hpp" + +namespace DetourNavigator +{ + struct TilesPositionsRange + { + TilePosition mBegin; + TilePosition mEnd; + }; +} + +#endif diff --git a/components/serialization/format.hpp b/components/serialization/format.hpp index 595afd0dad..29fc0ec42d 100644 --- a/components/serialization/format.hpp +++ b/components/serialization/format.hpp @@ -7,6 +7,7 @@ #include <type_traits> #include <utility> #include <vector> +#include <cstdint> namespace Serialization { @@ -26,7 +27,7 @@ namespace Serialization struct IsContiguousContainer<std::array<T, n>> : std::true_type {}; template <class T> - constexpr bool isContiguousContainer = IsContiguousContainer<std::decay_t<T>>::value; + inline constexpr bool isContiguousContainer = IsContiguousContainer<std::decay_t<T>>::value; template <Mode mode, class Derived> struct Format @@ -51,13 +52,13 @@ namespace Serialization -> std::enable_if_t<isContiguousContainer<T>> { if constexpr (mode == Mode::Write) - visitor(self(), value.size()); + visitor(self(), static_cast<std::uint64_t>(value.size())); else { static_assert(mode == Mode::Read); - std::size_t size = 0; + std::uint64_t size = 0; visitor(self(), size); - value.resize(size); + value.resize(static_cast<std::size_t>(size)); } self()(std::forward<Visitor>(visitor), value.data(), value.size()); }