diff --git a/apps/benchmarks/detournavigator/navmeshtilescache.cpp b/apps/benchmarks/detournavigator/navmeshtilescache.cpp index dc3b31ce41..487562f404 100644 --- a/apps/benchmarks/detournavigator/navmeshtilescache.cpp +++ b/apps/benchmarks/detournavigator/navmeshtilescache.cpp @@ -15,13 +15,12 @@ namespace osg::Vec3f mAgentHalfExtents; TilePosition mTilePosition; RecastMesh mRecastMesh; - std::vector mOffMeshConnections; }; struct Item { Key mKey; - NavMeshData mValue; + PreparedNavMeshData mValue; }; template @@ -105,8 +104,7 @@ namespace generateWater(std::back_inserter(water), 2, random); RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices), std::move(areaTypes), std::move(water)); - std::vector offMeshConnections; - return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)}; + return Key {agentHalfExtents, tilePosition, std::move(recastMesh)}; } constexpr std::size_t trianglesPerTile = 310; @@ -125,7 +123,8 @@ namespace while (true) { Key key = generateKey(trianglesPerTile, random); - cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData()); + cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, + std::make_unique()); *out++ = std::move(key); const std::size_t newSize = cache.getStats().mNavMeshCacheSize; if (size >= newSize) @@ -147,7 +146,7 @@ namespace while (state.KeepRunning()) { const auto& key = keys[n++ % keys.size()]; - const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections); + const auto result = cache.get(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh); benchmark::DoNotOptimize(result); } } @@ -175,7 +174,8 @@ namespace while (state.KeepRunning()) { const auto& key = keys[n++ % keys.size()]; - const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, key.mOffMeshConnections, NavMeshData()); + const auto result = cache.set(key.mAgentHalfExtents, key.mTilePosition, key.mRecastMesh, + std::make_unique()); benchmark::DoNotOptimize(result); } } diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index 447a5b44e4..17c1b955cf 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -3,24 +3,131 @@ #include #include #include +#include +#include +#include + +#include #include #include -namespace DetourNavigator -{ - static inline bool operator ==(const NavMeshDataRef& lhs, const NavMeshDataRef& rhs) - { - return std::make_pair(lhs.mValue, lhs.mSize) == std::make_pair(rhs.mValue, rhs.mSize); - } -} +#include +#include namespace { using namespace testing; using namespace DetourNavigator; + void* permRecastAlloc(int size) + { + void* result = rcAlloc(static_cast(size), RC_ALLOC_PERM); + if (result == nullptr) + throw std::bad_alloc(); + return result; + } + + template + void generate(T*& values, int size) + { + values = static_cast(permRecastAlloc(size * sizeof(T))); + std::generate_n(values, static_cast(size), [] { return static_cast(std::rand()); }); + } + + void generate(rcPolyMesh& value, int size) + { + value.nverts = size; + value.maxpolys = size; + value.nvp = size; + value.npolys = size; + rcVcopy(value.bmin, osg::Vec3f(-1, -2, -3).ptr()); + rcVcopy(value.bmax, osg::Vec3f(3, 2, 1).ptr()); + value.cs = 1.0f / (std::rand() % 999 + 1); + value.ch = 1.0f / (std::rand() % 999 + 1); + value.borderSize = std::rand(); + value.maxEdgeError = 1.0f / (std::rand() % 999 + 1); + generate(value.verts, 3 * value.nverts); + generate(value.polys, value.maxpolys * 2 * value.nvp); + generate(value.regs, value.maxpolys); + generate(value.flags, value.maxpolys); + generate(value.areas, value.maxpolys); + } + + void generate(rcPolyMeshDetail& value, int size) + { + value.nmeshes = size; + value.nverts = size; + value.ntris = size; + generate(value.meshes, 4 * value.nmeshes); + generate(value.verts, 3 * value.nverts); + generate(value.tris, 4 * value.ntris); + } + + void generate(PreparedNavMeshData& value, int size) + { + value.mUserId = std::rand(); + value.mCellHeight = 1.0f / (std::rand() % 999 + 1); + value.mCellSize = 1.0f / (std::rand() % 999 + 1); + generate(value.mPolyMesh, size); + generate(value.mPolyMeshDetail, size); + } + + std::unique_ptr makePeparedNavMeshData(int size) + { + auto result = std::make_unique(); + generate(*result, size); + return result; + } + + template + void clone(const T* src, T*& dst, int size) + { + dst = static_cast(permRecastAlloc(size * sizeof(T))); + std::memcpy(dst, src, static_cast(size) * sizeof(T)); + } + + void clone(const rcPolyMesh& src, rcPolyMesh& dst) + { + dst.nverts = src.nverts; + dst.npolys = src.npolys; + dst.maxpolys = src.maxpolys; + dst.nvp = src.nvp; + rcVcopy(dst.bmin, src.bmin); + rcVcopy(dst.bmax, src.bmax); + dst.cs = src.cs; + dst.ch = src.ch; + dst.borderSize = src.borderSize; + dst.maxEdgeError = src.maxEdgeError; + clone(src.verts, dst.verts, 3 * dst.nverts); + clone(src.polys, dst.polys, dst.maxpolys * 2 * dst.nvp); + clone(src.regs, dst.regs, dst.maxpolys); + clone(src.flags, dst.flags, dst.maxpolys); + clone(src.areas, dst.areas, dst.maxpolys); + } + + void clone(const rcPolyMeshDetail& src, rcPolyMeshDetail& dst) + { + dst.nmeshes = src.nmeshes; + dst.nverts = src.nverts; + dst.ntris = src.ntris; + clone(src.meshes, dst.meshes, 4 * dst.nmeshes); + clone(src.verts, dst.verts, 3 * dst.nverts); + clone(src.tris, dst.tris, 4 * dst.ntris); + } + + std::unique_ptr clone(const PreparedNavMeshData& value) + { + auto result = std::make_unique(); + result->mUserId = value.mUserId; + result->mCellHeight = value.mCellHeight; + result->mCellSize = value.mCellSize; + clone(value.mPolyMesh, result->mPolyMesh); + clone(value.mPolyMeshDetail, result->mPolyMeshDetail); + return result; + } + struct DetourNavigatorNavMeshTilesCacheTest : Test { const osg::Vec3f mAgentHalfExtents {1, 2, 3}; @@ -32,17 +139,11 @@ namespace const std::vector mAreaTypes {1, AreaType_ground}; const std::vector mWater {}; const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater}; - const std::vector mOffMeshConnections {}; - unsigned char* const mData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData mNavMeshData {mData, 1}; - - const size_t cRecastMeshKeySize = mRecastMesh.getIndices().size() * sizeof(int) - + mRecastMesh.getVertices().size() * sizeof(float) - + mRecastMesh.getAreaTypes().size() * sizeof(AreaType) - + mRecastMesh.getWater().size() * sizeof(RecastMesh::Water) - + mOffMeshConnections.size() * sizeof(OffMeshConnection); + std::unique_ptr mPreparedNavMeshData {makePeparedNavMeshData(3)}; - const size_t cRecastMeshWithWaterKeySize = cRecastMeshKeySize + sizeof(RecastMesh::Water); + const std::size_t mRecastMeshSize = sizeof(mRecastMesh) + getSize(mRecastMesh); + const std::size_t mRecastMeshWithWaterSize = mRecastMeshSize + sizeof(RecastMesh::Water); + const std::size_t mPreparedNavMeshDataSize = sizeof(*mPreparedNavMeshData) + getSize(*mPreparedNavMeshData); }; TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_empty_cache_should_return_empty_value) @@ -50,7 +151,7 @@ namespace const std::size_t maxSize = 0; NavMeshTilesCache cache(maxSize); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_for_not_enought_cache_size_should_return_empty_value) @@ -58,51 +159,46 @@ namespace const std::size_t maxSize = 0; NavMeshTilesCache cache(maxSize); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData))); - EXPECT_NE(mNavMeshData.mValue, nullptr); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData))); + EXPECT_NE(mPreparedNavMeshData, nullptr); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); + const auto copy = clone(*mPreparedNavMeshData); + ASSERT_EQ(*mPreparedNavMeshData, *copy); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + EXPECT_EQ(result.get(), *copy); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_return_cached_element) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto copy = clone(*mPreparedNavMeshData); + const auto sameCopy = clone(*mPreparedNavMeshData); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_EQ(mNavMeshData.mValue, nullptr); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(anotherNavMeshData)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_EQ(mPreparedNavMeshData, nullptr); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(copy)); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + EXPECT_EQ(result.get(), *sameCopy); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); + const auto copy = clone(*mPreparedNavMeshData); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + const auto result = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + EXPECT_EQ(result.get(), *copy); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_agent_half_extents_should_return_empty_value) @@ -111,8 +207,8 @@ namespace NavMeshTilesCache cache(maxSize); const osg::Vec3f unexsistentAgentHalfExtents {1, 1, 1}; - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.get(unexsistentAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_tile_position_should_return_empty_value) @@ -121,8 +217,8 @@ namespace NavMeshTilesCache cache(maxSize); const TilePosition unexistentTilePosition {1, 1}; - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, unexistentTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_for_cache_miss_by_recast_mesh_should_return_empty_value) @@ -132,217 +228,189 @@ namespace const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); + const auto copy = clone(*anotherPreparedNavMeshData); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, + std::move(anotherPreparedNavMeshData)); ASSERT_TRUE(result); - EXPECT_EQ(result.get(), (NavMeshDataRef {anotherData, 1})); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_EQ(result.get(), *copy); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherPreparedNavMeshData = makePeparedNavMeshData(3); - const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); + const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, + std::move(anotherPreparedNavMeshData))); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_set_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); + const auto copy = clone(*mPreparedNavMeshData); const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, leastRecentlySetWater}; - const auto leastRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1}; + auto leastRecentlySetData = makePeparedNavMeshData(3); const std::vector mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mostRecentlySetWater}; - const auto mostRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1}; + auto mostRecentlySetData = makePeparedNavMeshData(3); - ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections, - std::move(leastRecentlySetNavMeshData))); - ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections, - std::move(mostRecentlySetNavMeshData))); + ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, + std::move(leastRecentlySetData))); + ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, + std::move(mostRecentlySetData))); - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); + EXPECT_EQ(result.get(), *copy); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh, mOffMeshConnections)); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlySetRecastMesh)); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlySetRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, leastRecentlyUsedWater}; - const auto leastRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1}; + auto leastRecentlyUsedData = makePeparedNavMeshData(3); + const auto leastRecentlyUsedCopy = clone(*leastRecentlyUsedData); const std::vector mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mostRecentlyUsedWater}; - const auto mostRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1}; + auto mostRecentlyUsedData = makePeparedNavMeshData(3); + const auto mostRecentlyUsedCopy = clone(*mostRecentlyUsedData); - cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections, - std::move(leastRecentlyUsedNavMeshData)); - cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections, - std::move(mostRecentlyUsedNavMeshData)); + cache.set(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, std::move(leastRecentlyUsedData)); + cache.set(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, std::move(mostRecentlyUsedData)); { - const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections); + const auto value = cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh); ASSERT_TRUE(value); - ASSERT_EQ(value.get(), (NavMeshDataRef {leastRecentlyUsedData, 1})); + ASSERT_EQ(value.get(), *leastRecentlyUsedCopy); } { - const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections); + const auto value = cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh); ASSERT_TRUE(value); - ASSERT_EQ(value.get(), (NavMeshDataRef {mostRecentlyUsedData, 1})); + ASSERT_EQ(value.get(), *mostRecentlyUsedCopy); } - const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); - EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); + const auto copy = clone(*mPreparedNavMeshData); + const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); + EXPECT_EQ(result.get(), *copy); - EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh, mOffMeshConnections)); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, leastRecentlyUsedRecastMesh)); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mostRecentlyUsedRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_cache_max_size) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); - NavMeshData tooLargeNavMeshData {tooLargeData, 2}; + auto tooLargeData = makePeparedNavMeshData(10); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections, - std::move(tooLargeNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, std::move(tooLargeData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize1 = cRecastMeshKeySize; - const std::size_t navMeshKeySize2 = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2; + const std::size_t maxSize = 2 * (mRecastMeshWithWaterSize + mPreparedNavMeshDataSize); NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherData = makePeparedNavMeshData(3); const std::vector tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, tooLargeWater}; - const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); - NavMeshData tooLargeNavMeshData {tooLargeData, 2}; + auto tooLargeData = makePeparedNavMeshData(10); - const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, - std::move(mNavMeshData)); + const auto value = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, + std::move(mPreparedNavMeshData)); ASSERT_TRUE(value); - ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, mOffMeshConnections, - std::move(tooLargeNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections)); + ASSERT_TRUE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, + std::move(anotherData))); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, tooLargeRecastMesh, + std::move(tooLargeData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, anotherRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherData = makePeparedNavMeshData(3); - const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); + const auto firstCopy = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); ASSERT_TRUE(firstCopy); { - const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(secondCopy); } - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available) { - const std::size_t navMeshDataSize = 1; - const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = mRecastMeshWithWaterSize + mPreparedNavMeshDataSize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; - const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); - NavMeshData anotherNavMeshData {anotherData, 1}; + auto anotherData = makePeparedNavMeshData(3); - cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); - const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, std::move(mPreparedNavMeshData)); + const auto firstCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(firstCopy); { - const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections); + const auto secondCopy = cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh); ASSERT_TRUE(secondCopy); } - EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, mOffMeshConnections, - std::move(anotherNavMeshData))); - EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections)); + EXPECT_FALSE(cache.set(mAgentHalfExtents, mTilePosition, anotherRecastMesh, std::move(anotherData))); + EXPECT_TRUE(cache.get(mAgentHalfExtents, mTilePosition, mRecastMesh)); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9d72320ca9..678a4ea02f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -193,6 +193,8 @@ add_component_dir(detournavigator navmeshtileview oscillatingrecastmeshobject offmeshconnectionsmanager + preparednavmeshdata + navmeshcacheitem ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 422fdffb1d..507ca078e9 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -7,6 +7,8 @@ #include "sharednavmesh.hpp" #include "flags.hpp" #include "navmeshtilescache.hpp" +#include "preparednavmeshdata.hpp" +#include "navmeshdata.hpp" #include @@ -26,25 +28,6 @@ namespace { using namespace DetourNavigator; - void initPolyMeshDetail(rcPolyMeshDetail& value) - { - value.meshes = nullptr; - value.verts = nullptr; - value.tris = nullptr; - } - - struct PolyMeshDetailStackDeleter - { - void operator ()(rcPolyMeshDetail* value) const - { - rcFree(value->meshes); - rcFree(value->verts); - rcFree(value->tris); - } - }; - - using PolyMeshDetailStackPtr = std::unique_ptr; - struct WaterBounds { osg::Vec3f mMin; @@ -363,10 +346,25 @@ namespace return true; } - NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, - const std::vector& offMeshConnections, const TilePosition& tile, - const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings) + template + unsigned long getMinValuableBitsNumber(const T value) + { + unsigned long power = 0; + while (power < sizeof(T) * 8 && (static_cast(1) << power) < value) + ++power; + return power; + } +} + +namespace DetourNavigator +{ + std::unique_ptr prepareNavMeshTileData(const RecastMesh& recastMesh, + const TilePosition& tile, const Bounds& bounds, const osg::Vec3f& agentHalfExtents, const Settings& settings) { + const TileBounds tileBounds = makeTileBounds(settings, tile); + const osg::Vec3f boundsMin(tileBounds.mMin.x(), bounds.mMin.y() - 1, tileBounds.mMin.y()); + const osg::Vec3f boundsMax(tileBounds.mMax.x(), bounds.mMax.y() + 1, tileBounds.mMax.y()); + rcContext context; const auto config = makeConfig(agentHalfExtents, boundsMin, boundsMax, settings); @@ -374,19 +372,27 @@ namespace createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch); if (!rasterizeTriangles(context, agentHalfExtents, recastMesh, config, settings, solid)) - return NavMeshData(); + return nullptr; rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid); rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid); rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid); - rcPolyMesh polyMesh; - rcPolyMeshDetail polyMeshDetail; - initPolyMeshDetail(polyMeshDetail); - const PolyMeshDetailStackPtr polyMeshDetailPtr(&polyMeshDetail); - if (!fillPolyMesh(context, config, solid, polyMesh, polyMeshDetail)) - return NavMeshData(); + std::unique_ptr result = std::make_unique(); + if (!fillPolyMesh(context, config, solid, result->mPolyMesh, result->mPolyMeshDetail)) + return nullptr; + + result->mCellSize = config.cs; + result->mCellHeight = config.ch; + + return result; + } + + NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, + const std::vector& offMeshConnections, const osg::Vec3f& agentHalfExtents, + const TilePosition& tile, const Settings& settings) + { const auto offMeshConVerts = getOffMeshVerts(offMeshConnections); const std::vector offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents)); const std::vector offMeshConDir(offMeshConnections.size(), 0); @@ -394,18 +400,18 @@ namespace const std::vector offMeshConFlags = getOffMeshFlags(offMeshConnections); dtNavMeshCreateParams params; - params.verts = polyMesh.verts; - params.vertCount = polyMesh.nverts; - params.polys = polyMesh.polys; - params.polyAreas = polyMesh.areas; - params.polyFlags = polyMesh.flags; - params.polyCount = polyMesh.npolys; - params.nvp = polyMesh.nvp; - params.detailMeshes = polyMeshDetail.meshes; - params.detailVerts = polyMeshDetail.verts; - params.detailVertsCount = polyMeshDetail.nverts; - params.detailTris = polyMeshDetail.tris; - params.detailTriCount = polyMeshDetail.ntris; + params.verts = data.mPolyMesh.verts; + params.vertCount = data.mPolyMesh.nverts; + params.polys = data.mPolyMesh.polys; + params.polyAreas = data.mPolyMesh.areas; + params.polyFlags = data.mPolyMesh.flags; + params.polyCount = data.mPolyMesh.npolys; + params.nvp = data.mPolyMesh.nvp; + params.detailMeshes = data.mPolyMeshDetail.meshes; + params.detailVerts = data.mPolyMeshDetail.verts; + params.detailVertsCount = data.mPolyMeshDetail.nverts; + params.detailTris = data.mPolyMeshDetail.tris; + params.detailTriCount = data.mPolyMeshDetail.ntris; params.offMeshConVerts = offMeshConVerts.data(); params.offMeshConRad = offMeshConRad.data(); params.offMeshConDir = offMeshConDir.data(); @@ -416,12 +422,12 @@ namespace params.walkableHeight = getHeight(settings, agentHalfExtents); params.walkableRadius = getRadius(settings, agentHalfExtents); params.walkableClimb = getMaxClimb(settings); - rcVcopy(params.bmin, polyMesh.bmin); - rcVcopy(params.bmax, polyMesh.bmax); - params.cs = config.cs; - params.ch = config.ch; + rcVcopy(params.bmin, data.mPolyMesh.bmin); + rcVcopy(params.bmax, data.mPolyMesh.bmax); + params.cs = data.mCellSize; + params.ch = data.mCellHeight; params.buildBvTree = true; - params.userId = 0; + params.userId = data.mUserId; params.tileX = tile.x(); params.tileY = tile.y(); params.tileLayer = 0; @@ -436,20 +442,6 @@ namespace return NavMeshData(navMeshData, navMeshDataSize); } - - - template - unsigned long getMinValuableBitsNumber(const T value) - { - unsigned long power = 0; - while (power < sizeof(T) * 8 && (static_cast(1) << power) < value) - ++power; - return power; - } -} - -namespace DetourNavigator -{ NavMeshPtr makeEmptyNavMesh(const Settings& settings) { // Max tiles and max polys affect how the tile IDs are caculated. @@ -522,35 +514,32 @@ namespace DetourNavigator return navMeshCacheItem->lock()->removeTile(changedTile); } - auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections); + auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh); bool cached = static_cast(cachedNavMeshData); if (!cachedNavMeshData) { - const auto tileBounds = makeTileBounds(settings, changedTile); - const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), recastMeshBounds.mMin.y() - 1, tileBounds.mMin.y()); - const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), recastMeshBounds.mMax.y() + 1, tileBounds.mMax.y()); - - auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile, - tileBorderMin, tileBorderMax, settings); + auto prepared = prepareNavMeshTileData(*recastMesh, changedTile, recastMeshBounds, + agentHalfExtents, settings); - if (!navMeshData.mValue) + if (prepared == nullptr) { Log(Debug::Debug) << "Ignore add tile: NavMeshData is null"; return navMeshCacheItem->lock()->removeTile(changedTile); } - cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh, - offMeshConnections, std::move(navMeshData)); + cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh, std::move(prepared)); if (!cachedNavMeshData) { Log(Debug::Debug) << "Navigator cache overflow"; - return navMeshCacheItem->lock()->updateTile(changedTile, std::move(navMeshData)); + return navMeshCacheItem->lock()->updateTile(changedTile, NavMeshTilesCache::Value(), + makeNavMeshTileData(*prepared, offMeshConnections, agentHalfExtents, changedTile, settings)); } } - const auto updateStatus = navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData)); + const auto updateStatus = navMeshCacheItem->lock()->updateTile(changedTile, std::move(cachedNavMeshData), + makeNavMeshTileData(cachedNavMeshData.get(), offMeshConnections, agentHalfExtents, changedTile, settings)); return UpdateNavMeshStatusBuilder(updateStatus).cached(cached).getResult(); } diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index 95720634cd..3e07341106 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -6,10 +6,12 @@ #include "tileposition.hpp" #include "sharednavmesh.hpp" #include "navmeshtilescache.hpp" +#include "offmeshconnection.hpp" #include #include +#include class dtNavMesh; @@ -17,6 +19,8 @@ namespace DetourNavigator { class RecastMesh; struct Settings; + struct PreparedNavMeshData; + struct NavMeshData; inline float getLength(const osg::Vec2i& value) { @@ -34,6 +38,13 @@ namespace DetourNavigator return expectedTilesCount <= maxTiles; } + std::unique_ptr prepareNavMeshTileData(const RecastMesh& recastMesh, const TilePosition& tile, + const Bounds& bounds, const osg::Vec3f& agentHalfExtents, const Settings& settings); + + NavMeshData makeNavMeshTileData(const PreparedNavMeshData& data, + const std::vector& offMeshConnections, const osg::Vec3f& agentHalfExtents, + const TilePosition& tile, const Settings& settings); + NavMeshPtr makeEmptyNavMesh(const Settings& settings); UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, diff --git a/components/detournavigator/navmeshcacheitem.cpp b/components/detournavigator/navmeshcacheitem.cpp new file mode 100644 index 0000000000..ee6f3308d0 --- /dev/null +++ b/components/detournavigator/navmeshcacheitem.cpp @@ -0,0 +1,82 @@ +#include "tileposition.hpp" +#include "navmeshtilescache.hpp" +#include "dtstatus.hpp" +#include "navmeshtileview.hpp" +#include "navmeshcacheitem.hpp" +#include "navmeshdata.hpp" + +#include + +#include + +namespace +{ + using DetourNavigator::TilePosition; + + const dtMeshTile* getTile(const dtNavMesh& navMesh, const TilePosition& position) + { + const int layer = 0; + return navMesh.getTileAt(position.x(), position.y(), layer); + } + + bool removeTile(dtNavMesh& navMesh, const TilePosition& position) + { + const int layer = 0; + const auto tileRef = navMesh.getTileRefAt(position.x(), position.y(), layer); + if (tileRef == 0) + return false; + unsigned char** const data = nullptr; + int* const dataSize = nullptr; + return dtStatusSucceed(navMesh.removeTile(tileRef, data, dataSize)); + } + + dtStatus addTile(dtNavMesh& navMesh, unsigned char* data, int size) + { + const int doNotTransferOwnership = 0; + const dtTileRef lastRef = 0; + dtTileRef* const result = nullptr; + return navMesh.addTile(data, size, doNotTransferOwnership, lastRef, result); + } +} + +namespace DetourNavigator +{ + UpdateNavMeshStatus NavMeshCacheItem::updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached, + NavMeshData&& navMeshData) + { + const dtMeshTile* currentTile = ::getTile(*mImpl, position); + if (currentTile != nullptr + && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(navMeshData.mValue.get())) + { + return UpdateNavMeshStatus::ignored; + } + const auto removed = ::removeTile(*mImpl, position); + const auto addStatus = addTile(*mImpl, navMeshData.mValue.get(), navMeshData.mSize); + if (dtStatusSucceed(addStatus)) + { + mUsedTiles[position] = std::make_pair(std::move(cached), std::move(navMeshData)); + ++mNavMeshRevision; + return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); + } + else + { + if (removed) + { + mUsedTiles.erase(position); + ++mNavMeshRevision; + } + return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); + } + } + + UpdateNavMeshStatus NavMeshCacheItem::removeTile(const TilePosition& position) + { + const auto removed = ::removeTile(*mImpl, position); + if (removed) + { + mUsedTiles.erase(position); + ++mNavMeshRevision; + } + return UpdateNavMeshStatusBuilder().removed(removed).getResult(); + } +} diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index a9a4ebee62..5558f4e1f5 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -5,14 +5,14 @@ #include "tileposition.hpp" #include "navmeshtilescache.hpp" #include "dtstatus.hpp" -#include "navmeshtileview.hpp" +#include "navmeshdata.hpp" #include -#include - #include +struct dtMeshTile; + namespace DetourNavigator { enum class UpdateNavMeshStatus : unsigned @@ -96,26 +96,6 @@ namespace DetourNavigator } }; - inline unsigned char* getRawData(NavMeshData& navMeshData) - { - return navMeshData.mValue.get(); - } - - inline unsigned char* getRawData(NavMeshTilesCache::Value& cachedNavMeshData) - { - return cachedNavMeshData.get().mValue; - } - - inline int getSize(const NavMeshData& navMeshData) - { - return navMeshData.mSize; - } - - inline int getSize(const NavMeshTilesCache::Value& cachedNavMeshData) - { - return cachedNavMeshData.get().mSize; - } - class NavMeshCacheItem { public: @@ -139,86 +119,16 @@ namespace DetourNavigator return mNavMeshRevision; } - template - UpdateNavMeshStatus updateTile(const TilePosition& position, T&& navMeshData) - { - const dtMeshTile* currentTile = getTile(position); - if (currentTile != nullptr - && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(getRawData(navMeshData))) - { - return UpdateNavMeshStatus::ignored; - } - const auto removed = removeTileImpl(position); - const auto addStatus = addTileImpl(getRawData(navMeshData), getSize(navMeshData)); - if (dtStatusSucceed(addStatus)) - { - setUsedTile(position, std::forward(navMeshData)); - return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); - } - else - { - if (removed) - removeUsedTile(position); - return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); - } - } + UpdateNavMeshStatus updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached, + NavMeshData&& navMeshData); - UpdateNavMeshStatus removeTile(const TilePosition& position) - { - const auto removed = removeTileImpl(position); - if (removed) - removeUsedTile(position); - return UpdateNavMeshStatusBuilder().removed(removed).getResult(); - } + UpdateNavMeshStatus removeTile(const TilePosition& position); private: NavMeshPtr mImpl; std::size_t mGeneration; std::size_t mNavMeshRevision; std::map> mUsedTiles; - - void setUsedTile(const TilePosition& tilePosition, NavMeshTilesCache::Value value) - { - mUsedTiles[tilePosition] = std::make_pair(std::move(value), NavMeshData()); - ++mNavMeshRevision; - } - - void setUsedTile(const TilePosition& tilePosition, NavMeshData value) - { - mUsedTiles[tilePosition] = std::make_pair(NavMeshTilesCache::Value(), std::move(value)); - ++mNavMeshRevision; - } - - void removeUsedTile(const TilePosition& tilePosition) - { - mUsedTiles.erase(tilePosition); - ++mNavMeshRevision; - } - - dtStatus addTileImpl(unsigned char* data, int size) - { - const int doNotTransferOwnership = 0; - const dtTileRef lastRef = 0; - dtTileRef* const result = nullptr; - return mImpl->addTile(data, size, doNotTransferOwnership, lastRef, result); - } - - bool removeTileImpl(const TilePosition& position) - { - const int layer = 0; - const auto tileRef = mImpl->getTileRefAt(position.x(), position.y(), layer); - if (tileRef == 0) - return false; - unsigned char** const data = nullptr; - int* const dataSize = nullptr; - return dtStatusSucceed(mImpl->removeTile(tileRef, data, dataSize)); - } - - const dtMeshTile* getTile(const TilePosition& position) const - { - const int layer = 0; - return mImpl->getTileAt(position.x(), position.y(), layer); - } }; using GuardedNavMeshCacheItem = Misc::ScopeGuarded; diff --git a/components/detournavigator/navmeshdata.hpp b/components/detournavigator/navmeshdata.hpp index 8ce79614b3..501e971cba 100644 --- a/components/detournavigator/navmeshdata.hpp +++ b/components/detournavigator/navmeshdata.hpp @@ -3,7 +3,6 @@ #include -#include #include namespace DetourNavigator diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index ccc41a666d..7c29f734ea 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -6,32 +6,18 @@ namespace DetourNavigator { - namespace - { - inline std::size_t getSize(const RecastMesh& recastMesh, - const std::vector& offMeshConnections) - { - const std::size_t indicesSize = recastMesh.getIndices().size() * sizeof(int); - const std::size_t verticesSize = recastMesh.getVertices().size() * sizeof(float); - const std::size_t areaTypesSize = recastMesh.getAreaTypes().size() * sizeof(AreaType); - const std::size_t waterSize = recastMesh.getWater().size() * sizeof(RecastMesh::Water); - const std::size_t offMeshConnectionsSize = offMeshConnections.size() * sizeof(OffMeshConnection); - return indicesSize + verticesSize + areaTypesSize + waterSize + offMeshConnectionsSize; - } - } - NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize) : mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0), mHitCount(0), mGetCount(0) {} NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections) + const RecastMesh& recastMesh) { const std::lock_guard lock(mMutex); ++mGetCount; - const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyView(recastMesh, offMeshConnections))); + const auto tile = mValues.find(std::make_tuple(agentHalfExtents, changedTile, recastMesh)); if (tile == mValues.end()) return Value(); @@ -43,10 +29,10 @@ namespace DetourNavigator } NavMeshTilesCache::Value NavMeshTilesCache::set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections, - NavMeshData&& value) + const RecastMesh& recastMesh, std::unique_ptr&& value) { - const auto itemSize = static_cast(value.mSize) + getSize(recastMesh, offMeshConnections); + const auto itemSize = sizeof(RecastMesh) + getSize(recastMesh) + + (value == nullptr ? 0 : sizeof(PreparedNavMeshData) + getSize(*value)); const std::lock_guard lock(mMutex); @@ -56,13 +42,10 @@ namespace DetourNavigator while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) removeLeastRecentlyUsed(); - NavMeshKey navMeshKey { - RecastMeshData {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()}, - offMeshConnections - }; + RecastMeshData key {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()}; - const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey), itemSize); - const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, NavMeshKeyRef(iterator->mNavMeshKey)), iterator); + const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(key), itemSize); + const auto emplaced = mValues.emplace(std::make_tuple(agentHalfExtents, changedTile, std::cref(iterator->mRecastMeshData)), iterator); if (!emplaced.second) { @@ -73,7 +56,7 @@ namespace DetourNavigator return Value(*this, emplaced.first->second); } - iterator->mNavMeshData = std::move(value); + iterator->mPreparedNavMeshData = std::move(value); ++iterator->mUseCount; mUsedNavMeshDataSize += itemSize; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); @@ -108,7 +91,7 @@ namespace DetourNavigator { const auto& item = mFreeItems.back(); - const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, NavMeshKeyRef(item.mNavMeshKey))); + const auto value = mValues.find(std::make_tuple(item.mAgentHalfExtents, item.mChangedTile, std::cref(item.mRecastMeshData))); if (value == mValues.end()) return; diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index afa53beba0..37c0be7211 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -1,8 +1,7 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVMESHTILESCACHE_H -#include "offmeshconnection.hpp" -#include "navmeshdata.hpp" +#include "preparednavmeshdata.hpp" #include "recastmesh.hpp" #include "tileposition.hpp" @@ -21,12 +20,6 @@ namespace osg namespace DetourNavigator { - struct NavMeshDataRef - { - unsigned char* mValue; - int mSize; - }; - struct RecastMeshData { std::vector mIndices; @@ -53,71 +46,6 @@ namespace DetourNavigator < std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater); } - struct NavMeshKey - { - RecastMeshData mRecastMesh; - std::vector mOffMeshConnections; - }; - - inline bool operator <(const NavMeshKey& lhs, const NavMeshKey& rhs) - { - return std::tie(lhs.mRecastMesh, lhs.mOffMeshConnections) - < std::tie(rhs.mRecastMesh, rhs.mOffMeshConnections); - } - - struct NavMeshKeyRef - { - std::reference_wrapper mRef; - - explicit NavMeshKeyRef(const NavMeshKey& ref) : mRef(ref) {} - }; - - inline bool operator <(const NavMeshKeyRef& lhs, const NavMeshKeyRef& rhs) - { - return lhs.mRef.get() < rhs.mRef.get(); - } - - struct NavMeshKeyView - { - std::reference_wrapper mRecastMesh; - std::reference_wrapper> mOffMeshConnections; - - NavMeshKeyView(const RecastMesh& recastMesh, const std::vector& offMeshConnections) - : mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {} - }; - - inline bool operator <(const NavMeshKeyView& lhs, const NavMeshKey& rhs) - { - return std::tie(lhs.mRecastMesh.get(), lhs.mOffMeshConnections.get()) - < std::tie(rhs.mRecastMesh, rhs.mOffMeshConnections); - } - - inline bool operator <(const NavMeshKey& lhs, const NavMeshKeyView& rhs) - { - return std::tie(lhs.mRecastMesh, lhs.mOffMeshConnections) - < std::tie(rhs.mRecastMesh.get(), rhs.mOffMeshConnections.get()); - } - - template - inline bool operator <(const NavMeshKeyRef& lhs, const R& rhs) - { - return lhs.mRef.get() < rhs; - } - - template - inline bool operator <(const L& lhs, const NavMeshKeyRef& rhs) - { - return lhs < rhs.mRef.get(); - } - - template - inline bool operator <(const std::tuple& lhs, const std::tuple& rhs) - { - const auto left = std::tie(std::get<0>(lhs), std::get<1>(lhs)); - const auto right = std::tie(std::get<0>(rhs), std::get<1>(rhs)); - return std::tie(left, std::get<2>(lhs)) < std::tie(right, std::get<2>(rhs)); - } - class NavMeshTilesCache { public: @@ -126,15 +54,16 @@ namespace DetourNavigator std::atomic mUseCount; osg::Vec3f mAgentHalfExtents; TilePosition mChangedTile; - NavMeshKey mNavMeshKey; - NavMeshData mNavMeshData; + RecastMeshData mRecastMeshData; + std::unique_ptr mPreparedNavMeshData; std::size_t mSize; - Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, NavMeshKey&& navMeshKey, std::size_t size) + Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, + RecastMeshData&& recastMeshData, std::size_t size) : mUseCount(0) , mAgentHalfExtents(agentHalfExtents) , mChangedTile(changedTile) - , mNavMeshKey(navMeshKey) + , mRecastMeshData(std::move(recastMeshData)) , mSize(size) {} }; @@ -181,9 +110,9 @@ namespace DetourNavigator return *this; } - NavMeshDataRef get() const + const PreparedNavMeshData& get() const { - return NavMeshDataRef {mIterator->mNavMeshData.mValue.get(), mIterator->mNavMeshData.mSize}; + return *mIterator->mPreparedNavMeshData; } operator bool() const @@ -208,11 +137,10 @@ namespace DetourNavigator NavMeshTilesCache(const std::size_t maxNavMeshDataSize); Value get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections); + const RecastMesh& recastMesh); Value set(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, - const RecastMesh& recastMesh, const std::vector& offMeshConnections, - NavMeshData&& value); + const RecastMesh& recastMesh, std::unique_ptr&& value); Stats getStats() const; @@ -227,7 +155,7 @@ namespace DetourNavigator std::size_t mGetCount; std::list mBusyItems; std::list mFreeItems; - std::map, ItemIterator, std::less<>> mValues; + std::map>, ItemIterator, std::less<>> mValues; void removeLeastRecentlyUsed(); diff --git a/components/detournavigator/navmeshtileview.cpp b/components/detournavigator/navmeshtileview.cpp index 96f07c6b1c..336cd1ba84 100644 --- a/components/detournavigator/navmeshtileview.cpp +++ b/components/detournavigator/navmeshtileview.cpp @@ -1,4 +1,5 @@ #include "navmeshtileview.hpp" +#include "ref.hpp" #include #include @@ -10,47 +11,9 @@ namespace { - template - struct Ref - { - T& mRef; - - explicit Ref(T& ref) : mRef(ref) {} - - friend bool operator==(const Ref& lhs, const Ref& rhs) - { - return lhs.mRef == rhs.mRef; - } - }; - - template - struct ArrayRef - { - T (&mRef)[size]; - - explicit ArrayRef(T (&ref)[size]) : mRef(ref) {} - - friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs) - { - return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef)); - } - }; - - template - struct Span - { - T* mBegin; - T* mEnd; - - explicit Span(T* data, int size) : mBegin(data), mEnd(data + static_cast(size)) {} - - friend bool operator==(const Span& lhs, const Span& rhs) - { - // size is already equal if headers are equal - assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin)); - return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin); - } - }; + using DetourNavigator::ArrayRef; + using DetourNavigator::Ref; + using DetourNavigator::Span; auto makeTuple(const dtMeshHeader& v) { diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index 01bae02732..9312bc6c48 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -5,6 +5,7 @@ #include +#include #include namespace DetourNavigator diff --git a/components/detournavigator/preparednavmeshdata.cpp b/components/detournavigator/preparednavmeshdata.cpp new file mode 100644 index 0000000000..3fea46b26c --- /dev/null +++ b/components/detournavigator/preparednavmeshdata.cpp @@ -0,0 +1,52 @@ +#include "preparednavmeshdata.hpp" +#include "preparednavmeshdatatuple.hpp" + +#include +#include + +namespace +{ + using namespace DetourNavigator; + + void initPolyMeshDetail(rcPolyMeshDetail& value) noexcept + { + value.meshes = nullptr; + value.verts = nullptr; + value.tris = nullptr; + value.nmeshes = 0; + value.nverts = 0; + value.ntris = 0; + } + + void freePolyMeshDetail(rcPolyMeshDetail& value) noexcept + { + rcFree(value.meshes); + rcFree(value.verts); + rcFree(value.tris); + } +} + +template +inline constexpr auto operator==(const T& lhs, const T& rhs) noexcept + -> std::enable_if_t, void>, bool> +{ + return makeTuple(lhs) == makeTuple(rhs); +} + +namespace DetourNavigator +{ + PreparedNavMeshData::PreparedNavMeshData() noexcept + { + initPolyMeshDetail(mPolyMeshDetail); + } + + PreparedNavMeshData::~PreparedNavMeshData() noexcept + { + freePolyMeshDetail(mPolyMeshDetail); + } + + bool operator==(const PreparedNavMeshData& lhs, const PreparedNavMeshData& rhs) noexcept + { + return makeTuple(lhs) == makeTuple(rhs); + } +} diff --git a/components/detournavigator/preparednavmeshdata.hpp b/components/detournavigator/preparednavmeshdata.hpp new file mode 100644 index 0000000000..4a92265231 --- /dev/null +++ b/components/detournavigator/preparednavmeshdata.hpp @@ -0,0 +1,48 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATA_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATA_H + +#include + +#include + +namespace DetourNavigator +{ + struct PreparedNavMeshData + { + unsigned int mUserId = 0; + float mCellSize = 0; + float mCellHeight = 0; + rcPolyMesh mPolyMesh; + rcPolyMeshDetail mPolyMeshDetail; + + PreparedNavMeshData() noexcept; + PreparedNavMeshData(const PreparedNavMeshData&) = delete; + + ~PreparedNavMeshData() noexcept; + + friend bool operator==(const PreparedNavMeshData& lhs, const PreparedNavMeshData& rhs) noexcept; + }; + + inline constexpr std::size_t getSize(const rcPolyMesh& value) noexcept + { + return static_cast(3 * value.nverts) * sizeof(*value.verts) + + static_cast(value.maxpolys * 2 * value.nvp) * sizeof(*value.polys) + + static_cast(value.maxpolys) * sizeof(*value.regs) + + static_cast(value.maxpolys) * sizeof(*value.flags) + + static_cast(value.maxpolys) * sizeof(*value.areas); + } + + inline constexpr std::size_t getSize(const rcPolyMeshDetail& value) noexcept + { + return static_cast(4 * value.nmeshes) * sizeof(*value.meshes) + + static_cast(4 * value.ntris) * sizeof(*value.tris) + + static_cast(3 * value.nverts) * sizeof(*value.verts); + } + + inline constexpr std::size_t getSize(const PreparedNavMeshData& value) noexcept + { + return getSize(value.mPolyMesh) + getSize(value.mPolyMeshDetail); + } +} + +#endif diff --git a/components/detournavigator/preparednavmeshdatatuple.hpp b/components/detournavigator/preparednavmeshdatatuple.hpp new file mode 100644 index 0000000000..5b5a72f629 --- /dev/null +++ b/components/detournavigator/preparednavmeshdatatuple.hpp @@ -0,0 +1,51 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATATUPLE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_PREPAREDNAVMESHDATATUPLE_H + +#include "preparednavmeshdata.hpp" +#include "ref.hpp" + +#include + +#include + +namespace DetourNavigator +{ + constexpr auto makeTuple(const rcPolyMesh& v) noexcept + { + return std::tuple( + Span(v.verts, 3 * v.nverts), + Span(v.polys, v.maxpolys * 2 * v.nvp), + Span(v.regs, v.maxpolys), + Span(v.flags, v.maxpolys), + Span(v.areas, v.maxpolys), + ArrayRef(v.bmin), + ArrayRef(v.bmax), + v.cs, + v.ch, + v.borderSize, + v.maxEdgeError + ); + } + + constexpr auto makeTuple(const rcPolyMeshDetail& v) noexcept + { + return std::tuple( + Span(v.meshes, 4 * v.nmeshes), + Span(v.verts, 3 * v.nverts), + Span(v.tris, 4 * v.ntris) + ); + } + + constexpr auto makeTuple(const PreparedNavMeshData& v) noexcept + { + return std::tuple( + v.mUserId, + v.mCellHeight, + v.mCellSize, + Ref(v.mPolyMesh), + Ref(v.mPolyMeshDetail) + ); + } +} + +#endif diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 0e6bc49204..7f5758a85e 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -80,6 +80,15 @@ namespace DetourNavigator std::vector mAreaTypes; std::vector mWater; Bounds mBounds; + + friend inline std::size_t getSize(const RecastMesh& recastMesh) noexcept + { + const std::size_t indicesSize = recastMesh.mIndices.size() * sizeof(int); + const std::size_t verticesSize = recastMesh.mVertices.size() * sizeof(float); + const std::size_t areaTypesSize = recastMesh.mAreaTypes.size() * sizeof(AreaType); + const std::size_t waterSize = recastMesh.mWater.size() * sizeof(RecastMesh::Water); + return indicesSize + verticesSize + areaTypesSize + waterSize; + } }; inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) diff --git a/components/detournavigator/ref.hpp b/components/detournavigator/ref.hpp new file mode 100644 index 0000000000..d800bde8f8 --- /dev/null +++ b/components/detournavigator/ref.hpp @@ -0,0 +1,55 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_REF_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_REF_H + +#include +#include + +namespace DetourNavigator +{ + template + struct Ref + { + T& mRef; + + constexpr explicit Ref(T& ref) noexcept : mRef(ref) {} + + friend bool operator==(const Ref& lhs, const Ref& rhs) + { + return lhs.mRef == rhs.mRef; + } + }; + + template + struct ArrayRef + { + T (&mRef)[size]; + + constexpr explicit ArrayRef(T (&ref)[size]) noexcept : mRef(ref) {} + + friend bool operator==(const ArrayRef& lhs, const ArrayRef& rhs) + { + return std::equal(std::begin(lhs.mRef), std::end(lhs.mRef), std::begin(rhs.mRef)); + } + }; + + template + struct Span + { + T* mBegin; + T* mEnd; + + constexpr explicit Span(T* data, int size) noexcept + : mBegin(data) + , mEnd(data + static_cast(size)) + {} + + friend bool operator==(const Span& lhs, const Span& rhs) + { + // size is already equal if headers are equal + assert((lhs.mEnd - lhs.mBegin) == (rhs.mEnd - rhs.mBegin)); + return std::equal(lhs.mBegin, lhs.mEnd, rhs.mBegin); + } + }; +} + +#endif