1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 18:19:55 +00:00

Merge branch 'fix_navmesh_cache_key' into 'master'

Increase navmesh cache hit rate by fixing key implementation

See merge request OpenMW/openmw!584
This commit is contained in:
psi29a 2021-02-04 15:40:41 +00:00
commit f8c068ee34
9 changed files with 168 additions and 206 deletions

View file

@ -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<unsigned char*>(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<RecastMesh::Water> 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<RecastMesh::Water> 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<RecastMesh::Water> 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<RecastMesh::Water> 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<RecastMesh::Water> 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<RecastMesh::Water> 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<RecastMesh::Water> 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<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};

View file

@ -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

View file

@ -9,57 +9,29 @@ namespace DetourNavigator
{
namespace
{
inline std::vector<unsigned char> makeNavMeshKey(const RecastMesh& recastMesh,
const std::vector<OffMeshConnection>& offMeshConnections)
inline std::size_t getSize(const RecastMesh& recastMesh,
const std::vector<OffMeshConnection>& 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<unsigned char> 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<OffMeshConnection>& offMeshConnections)
{
const std::lock_guard<std::mutex> 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<OffMeshConnection>& offMeshConnections,
NavMeshData&& value)
{
const auto navMeshSize = static_cast<std::size_t>(value.mSize);
const auto itemSize = static_cast<std::size_t>(value.mSize) + getSize(recastMesh, offMeshConnections);
const std::lock_guard<std::mutex> 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<std::mutex> 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<double>(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<std::mutex> lock(mMutex);
mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator);
mFreeNavMeshDataSize += getSize(*iterator);
}
namespace
{
struct CompareBytes
{
const unsigned char* mRhsIt;
const unsigned char* const mRhsEnd;
template <class T>
int operator ()(const std::vector<T>& lhs)
{
const auto lhsBegin = reinterpret_cast<const char*>(lhs.data());
const auto lhsEnd = reinterpret_cast<const char*>(lhs.data() + lhs.size());
const auto lhsSize = static_cast<std::ptrdiff_t>(lhsEnd - lhsBegin);
const auto rhsSize = static_cast<std::ptrdiff_t>(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<unsigned char>& 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;
}
}

View file

@ -27,6 +27,89 @@ namespace DetourNavigator
int mSize;
};
struct RecastMeshData
{
std::vector<int> mIndices;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
std::vector<RecastMesh::Water> 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<OffMeshConnection> 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<const NavMeshKey> 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<const RecastMesh> mRecastMesh;
std::reference_wrapper<const std::vector<OffMeshConnection>> mOffMeshConnections;
NavMeshKeyView(const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& 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 <class R>
inline bool operator <(const NavMeshKeyRef& lhs, const R& rhs)
{
return lhs.mRef.get() < rhs;
}
template <class L>
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<std::int64_t> mUseCount;
osg::Vec3f mAgentHalfExtents;
TilePosition mChangedTile;
std::vector<unsigned char> mNavMeshKey;
NavMeshKey mNavMeshKey;
NavMeshData mNavMeshData;
std::size_t mSize;
Item(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile, std::vector<unsigned char>&& 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<unsigned char>& value)
: mValue(&value) {}
const std::vector<unsigned char>& getValue() const
{
assert(mValue);
return *mValue;
}
virtual int compare(const std::vector<unsigned char>& 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<unsigned char>* mValue = nullptr;
};
class RecastMeshKeyView : public KeyView
{
public:
RecastMeshKeyView(const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections)
: mRecastMesh(recastMesh), mOffMeshConnections(offMeshConnections) {}
int compare(const std::vector<unsigned char>& other) const override;
bool isLess(const KeyView& other) const override
{
return compare(other.getValue()) < 0;
}
virtual ~RecastMeshKeyView() = default;
private:
std::reference_wrapper<const RecastMesh> mRecastMesh;
std::reference_wrapper<const std::vector<OffMeshConnection>> mOffMeshConnections;
};
struct TileMap
{
std::map<KeyView, ItemIterator> mMap;
std::map<NavMeshKeyRef, ItemIterator, std::less<>> 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<Item> mBusyItems;
std::list<Item> mFreeItems;
std::map<osg::Vec3f, std::map<TilePosition, TileMap>> 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<std::size_t>(item.mNavMeshData.mSize) + 2 * item.mNavMeshKey.size();
}
};
}

View file

@ -5,6 +5,8 @@
#include <osg/Vec3f>
#include <tuple>
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

View file

@ -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;
}

View file

@ -5,9 +5,12 @@
#include "chunkytrimesh.hpp"
#include "bounds.hpp"
#include <components/bullethelpers/operators.hpp>
#include <memory>
#include <string>
#include <vector>
#include <tuple>
#include <osg/Vec3f>
@ -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

View file

@ -155,6 +155,7 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision)
{
optimizeRecastMesh(mIndices, mVertices);
std::sort(mWater.begin(), mWater.end());
return std::make_shared<RecastMesh>(generation, revision, mIndices, mVertices, mAreaTypes,
mWater, mSettings.get().mTrianglesPerChunk);
}

View file

@ -394,6 +394,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer)
"NavMesh CacheSize",
"NavMesh UsedTiles",
"NavMesh CachedTiles",
"NavMesh CacheHitRate",
"",
"Mechanics Actors",
"Mechanics Objects",