diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index e8e7820d91..5bc7af6467 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -68,7 +68,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, @@ -81,7 +81,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -97,7 +97,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); @@ -142,7 +142,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -163,7 +163,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -182,7 +182,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -214,7 +214,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshWithWaterKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -258,7 +258,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -277,7 +277,7 @@ namespace 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 + 2 * navMeshKeySize1 + 2 * navMeshKeySize2; + const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2; NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -306,7 +306,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -330,7 +330,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = cRecastMeshKeySize; - const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; diff --git a/components/bullethelpers/operators.hpp b/components/bullethelpers/operators.hpp index ea88deddf0..dd2ec8017c 100644 --- a/components/bullethelpers/operators.hpp +++ b/components/bullethelpers/operators.hpp @@ -68,4 +68,19 @@ inline std::ostream& operator <<(std::ostream& stream, BroadphaseNativeTypes val } } +inline bool operator <(const btVector3& lhs, const btVector3& rhs) +{ + return std::tie(lhs.x(), lhs.y(), lhs.z()) < std::tie(rhs.x(), rhs.y(), rhs.z()); +} + +inline bool operator <(const btMatrix3x3& lhs, const btMatrix3x3& rhs) +{ + return std::tie(lhs[0], lhs[1], lhs[2]) < std::tie(rhs[0], rhs[1], rhs[2]); +} + +inline bool operator <(const btTransform& lhs, const btTransform& rhs) +{ + return std::tie(lhs.getBasis(), lhs.getOrigin()) < std::tie(rhs.getBasis(), rhs.getOrigin()); +} + #endif diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index cff93ac0e5..b6048da589 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -9,57 +9,29 @@ namespace DetourNavigator { namespace { - inline std::vector makeNavMeshKey(const RecastMesh& recastMesh, - const std::vector& offMeshConnections) + 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); - - std::vector result(indicesSize + verticesSize + areaTypesSize + waterSize + offMeshConnectionsSize); - unsigned char* dst = result.data(); - - if (indicesSize > 0) - { - std::memcpy(dst, recastMesh.getIndices().data(), indicesSize); - dst += indicesSize; - } - - if (verticesSize > 0) - { - std::memcpy(dst, recastMesh.getVertices().data(), verticesSize); - dst += verticesSize; - } - - if (areaTypesSize > 0) - { - std::memcpy(dst, recastMesh.getAreaTypes().data(), areaTypesSize); - dst += areaTypesSize; - } - - if (waterSize > 0) - { - std::memcpy(dst, recastMesh.getWater().data(), waterSize); - dst += waterSize; - } - - if (offMeshConnectionsSize > 0) - std::memcpy(dst, offMeshConnections.data(), offMeshConnectionsSize); - - return result; + return indicesSize + verticesSize + areaTypesSize + waterSize + offMeshConnectionsSize; } } NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize) - : mMaxNavMeshDataSize(maxNavMeshDataSize), mUsedNavMeshDataSize(0), mFreeNavMeshDataSize(0) {} + : 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 std::lock_guard lock(mMutex); + ++mGetCount; + const auto agentValues = mValues.find(agentHalfExtents); if (agentValues == mValues.end()) return Value(); @@ -68,12 +40,14 @@ namespace DetourNavigator if (tileValues == agentValues->second.end()) return Value(); - const auto tile = tileValues->second.mMap.find(RecastMeshKeyView(recastMesh, offMeshConnections)); + const auto tile = tileValues->second.mMap.find(NavMeshKeyView(recastMesh, offMeshConnections)); if (tile == tileValues->second.mMap.end()) return Value(); acquireItemUnsafe(tile->second); + ++mHitCount; + return Value(*this, tile->second); } @@ -81,26 +55,22 @@ namespace DetourNavigator const RecastMesh& recastMesh, const std::vector& offMeshConnections, NavMeshData&& value) { - const auto navMeshSize = static_cast(value.mSize); + const auto itemSize = static_cast(value.mSize) + getSize(recastMesh, offMeshConnections); const std::lock_guard lock(mMutex); - if (navMeshSize > mMaxNavMeshDataSize) - return Value(); - - if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) - return Value(); - - auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections); - const auto itemSize = navMeshSize + 2 * navMeshKey.size(); - if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) return Value(); while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) removeLeastRecentlyUsed(); - const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey)); + NavMeshKey navMeshKey { + RecastMeshData {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()}, + offMeshConnections + }; + + const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey), itemSize); const auto emplaced = mValues[agentHalfExtents][changedTile].mMap.emplace(iterator->mNavMeshKey, iterator); if (!emplaced.second) @@ -123,17 +93,22 @@ namespace DetourNavigator std::size_t navMeshCacheSize = 0; std::size_t usedNavMeshTiles = 0; std::size_t cachedNavMeshTiles = 0; + std::size_t hitCount = 0; + std::size_t getCount = 0; { const std::lock_guard lock(mMutex); navMeshCacheSize = mUsedNavMeshDataSize; usedNavMeshTiles = mBusyItems.size(); cachedNavMeshTiles = mFreeItems.size(); + hitCount = mHitCount; + getCount = mGetCount; } stats.setAttribute(frameNumber, "NavMesh CacheSize", navMeshCacheSize); stats.setAttribute(frameNumber, "NavMesh UsedTiles", usedNavMeshTiles); stats.setAttribute(frameNumber, "NavMesh CachedTiles", cachedNavMeshTiles); + stats.setAttribute(frameNumber, "NavMesh CacheHitRate", static_cast(hitCount) / getCount * 100.0); } void NavMeshTilesCache::removeLeastRecentlyUsed() @@ -152,8 +127,8 @@ namespace DetourNavigator if (value == tileValues->second.mMap.end()) return; - mUsedNavMeshDataSize -= getSize(item); - mFreeNavMeshDataSize -= getSize(item); + mUsedNavMeshDataSize -= item.mSize; + mFreeNavMeshDataSize -= item.mSize; tileValues->second.mMap.erase(value); mFreeItems.pop_back(); @@ -174,7 +149,7 @@ namespace DetourNavigator return; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); - mFreeNavMeshDataSize -= getSize(*iterator); + mFreeNavMeshDataSize -= iterator->mSize; } void NavMeshTilesCache::releaseItem(ItemIterator iterator) @@ -185,71 +160,6 @@ namespace DetourNavigator const std::lock_guard lock(mMutex); mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator); - mFreeNavMeshDataSize += getSize(*iterator); - } - - namespace - { - struct CompareBytes - { - const unsigned char* mRhsIt; - const unsigned char* const mRhsEnd; - - template - int operator ()(const std::vector& lhs) - { - const auto lhsBegin = reinterpret_cast(lhs.data()); - const auto lhsEnd = reinterpret_cast(lhs.data() + lhs.size()); - const auto lhsSize = static_cast(lhsEnd - lhsBegin); - const auto rhsSize = static_cast(mRhsEnd - mRhsIt); - - if (lhsBegin == nullptr || mRhsIt == nullptr) - { - if (lhsSize < rhsSize) - return -1; - else if (lhsSize > rhsSize) - return 1; - else - return 0; - } - - const auto size = std::min(lhsSize, rhsSize); - - if (const auto result = std::memcmp(lhsBegin, mRhsIt, size)) - return result; - - if (lhsSize > rhsSize) - return 1; - - mRhsIt += size; - - return 0; - } - }; - } - - int NavMeshTilesCache::RecastMeshKeyView::compare(const std::vector& other) const - { - CompareBytes compareBytes {other.data(), other.data() + other.size()}; - - if (const auto result = compareBytes(mRecastMesh.get().getIndices())) - return result; - - if (const auto result = compareBytes(mRecastMesh.get().getVertices())) - return result; - - if (const auto result = compareBytes(mRecastMesh.get().getAreaTypes())) - return result; - - if (const auto result = compareBytes(mRecastMesh.get().getWater())) - return result; - - if (const auto result = compareBytes(mOffMeshConnections.get())) - return result; - - if (compareBytes.mRhsIt < compareBytes.mRhsEnd) - return -1; - - return 0; + mFreeNavMeshDataSize += iterator->mSize; } } diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 064d9e185b..25f4dc1878 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -27,6 +27,89 @@ namespace DetourNavigator int mSize; }; + struct RecastMeshData + { + std::vector mIndices; + std::vector mVertices; + std::vector mAreaTypes; + std::vector mWater; + }; + + inline bool operator <(const RecastMeshData& lhs, const RecastMeshData& rhs) + { + return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes, lhs.mWater) + < std::tie(rhs.mIndices, rhs.mVertices, rhs.mAreaTypes, rhs.mWater); + } + + inline bool operator <(const RecastMeshData& lhs, const RecastMesh& rhs) + { + return std::tie(lhs.mIndices, lhs.mVertices, lhs.mAreaTypes, lhs.mWater) + < std::tie(rhs.getIndices(), rhs.getVertices(), rhs.getAreaTypes(), rhs.getWater()); + } + + inline bool operator <(const RecastMesh& lhs, const RecastMeshData& rhs) + { + return std::tie(lhs.getIndices(), lhs.getVertices(), lhs.getAreaTypes(), lhs.getWater()) + < 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(); + } + class NavMeshTilesCache { public: @@ -35,14 +118,16 @@ namespace DetourNavigator std::atomic mUseCount; osg::Vec3f mAgentHalfExtents; TilePosition mChangedTile; - std::vector mNavMeshKey; + NavMeshKey mNavMeshKey; NavMeshData mNavMeshData; + std::size_t mSize; - Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, std::vector&& navMeshKey) + Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, NavMeshKey&& navMeshKey, std::size_t size) : mUseCount(0) , mAgentHalfExtents(agentHalfExtents) , mChangedTile(changedTile) - , mNavMeshKey(std::move(navMeshKey)) + , mNavMeshKey(navMeshKey) + , mSize(size) {} }; @@ -115,85 +200,17 @@ namespace DetourNavigator void reportStats(unsigned int frameNumber, osg::Stats& stats) const; private: - class KeyView - { - public: - KeyView() = default; - - virtual ~KeyView() = default; - - KeyView(const std::vector& value) - : mValue(&value) {} - - const std::vector& getValue() const - { - assert(mValue); - return *mValue; - } - - virtual int compare(const std::vector& other) const - { - assert(mValue); - - const auto valueSize = mValue->size(); - const auto otherSize = other.size(); - - if (const auto result = std::memcmp(mValue->data(), other.data(), std::min(valueSize, otherSize))) - return result; - - if (valueSize < otherSize) - return -1; - - if (valueSize > otherSize) - return 1; - - return 0; - } - - virtual bool isLess(const KeyView& other) const - { - assert(mValue); - return other.compare(*mValue) > 0; - } - - friend bool operator <(const KeyView& lhs, const KeyView& rhs) - { - return lhs.isLess(rhs); - } - - private: - const std::vector* mValue = nullptr; - }; - - class RecastMeshKeyView : public KeyView - { - public: - RecastMeshKeyView(const RecastMesh& recastMesh, const std::vector& offMeshConnections) - : mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {} - - int compare(const std::vector& other) const override; - - bool isLess(const KeyView& other) const override - { - return compare(other.getValue()) < 0; - } - - virtual ~RecastMeshKeyView() = default; - - private: - std::reference_wrapper mRecastMesh; - std::reference_wrapper> mOffMeshConnections; - }; - struct TileMap { - std::map mMap; + std::map> mMap; }; mutable std::mutex mMutex; std::size_t mMaxNavMeshDataSize; std::size_t mUsedNavMeshDataSize; std::size_t mFreeNavMeshDataSize; + std::size_t mHitCount; + std::size_t mGetCount; std::list mBusyItems; std::list mFreeItems; std::map> mValues; @@ -203,11 +220,6 @@ namespace DetourNavigator void acquireItemUnsafe(ItemIterator iterator); void releaseItem(ItemIterator iterator); - - static std::size_t getSize(const Item& item) - { - return static_cast(item.mNavMeshData.mSize) + 2 * item.mNavMeshKey.size(); - } }; } diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index ca999dbdb9..01bae02732 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -5,6 +5,8 @@ #include +#include + namespace DetourNavigator { struct OffMeshConnection @@ -13,6 +15,11 @@ namespace DetourNavigator osg::Vec3f mEnd; AreaType mAreaType; }; + + inline bool operator<(const OffMeshConnection& lhs, const OffMeshConnection& rhs) + { + return std::tie(lhs.mStart, lhs.mEnd, lhs.mAreaType) < std::tie(rhs.mStart, rhs.mEnd, rhs.mAreaType); + } } #endif diff --git a/components/detournavigator/offmeshconnectionsmanager.hpp b/components/detournavigator/offmeshconnectionsmanager.hpp index de707f3a86..1ad96e3b97 100644 --- a/components/detournavigator/offmeshconnectionsmanager.hpp +++ b/components/detournavigator/offmeshconnectionsmanager.hpp @@ -85,6 +85,8 @@ namespace DetourNavigator std::for_each(byId.first, byId.second, [&] (const auto& v) { result.push_back(v.second); }); }); + std::sort(result.begin(), result.end()); + return result; } diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index f3259903f3..746422ac82 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -5,9 +5,12 @@ #include "chunkytrimesh.hpp" #include "bounds.hpp" +#include + #include #include #include +#include #include @@ -87,6 +90,17 @@ namespace DetourNavigator ChunkyTriMesh mChunkyTriMesh; Bounds mBounds; }; + + inline bool operator<(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) + { + return std::tie(lhs.mCellSize, lhs.mTransform) < std::tie(rhs.mCellSize, rhs.mTransform); + } + + inline bool operator <(const RecastMesh& lhs, const RecastMesh& rhs) + { + return std::tie(lhs.getIndices(), lhs.getVertices(), lhs.getAreaTypes(), lhs.getWater()) + < std::tie(rhs.getIndices(), rhs.getVertices(), rhs.getAreaTypes(), rhs.getWater()); + } } #endif diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index ee014b9328..f8456acf0b 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -155,6 +155,7 @@ namespace DetourNavigator std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) { optimizeRecastMesh(mIndices, mVertices); + std::sort(mWater.begin(), mWater.end()); return std::make_shared(generation, revision, mIndices, mVertices, mAreaTypes, mWater, mSettings.get().mTrianglesPerChunk); } diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 3649af0896..690814f91d 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -394,6 +394,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "NavMesh CacheSize", "NavMesh UsedTiles", "NavMesh CachedTiles", + "NavMesh CacheHitRate", "", "Mechanics Actors", "Mechanics Objects",