mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-20 15:40:32 +00:00
Use raw recast mesh data and off mesh connections for navmesh key
Serialization into a vector of chars produces inconsistent results that leads to reduced cache hit rate. Using a structured object is a more clear solution and allows to remove serialization and nontrivial key compare logic with more straigt forward structured object comparison.
This commit is contained in:
parent
ad1f8c1e84
commit
3a2cea5271
@ -9,52 +9,21 @@ 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),
|
||||
mHitCount(0), mGetCount(0){}
|
||||
mHitCount(0), mGetCount(0) {}
|
||||
|
||||
NavMeshTilesCache::Value NavMeshTilesCache::get(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile,
|
||||
const RecastMesh& recastMesh, const std::vector<OffMeshConnection>& offMeshConnections)
|
||||
@ -71,7 +40,7 @@ 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();
|
||||
|
||||
@ -96,8 +65,11 @@ namespace DetourNavigator
|
||||
if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
||||
return Value();
|
||||
|
||||
auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections);
|
||||
const auto itemSize = navMeshSize + navMeshKey.size();
|
||||
NavMeshKey navMeshKey {
|
||||
RecastMeshData {recastMesh.getIndices(), recastMesh.getVertices(), recastMesh.getAreaTypes(), recastMesh.getWater()},
|
||||
offMeshConnections
|
||||
};
|
||||
const auto itemSize = navMeshSize + getSize(recastMesh, offMeshConnections);
|
||||
|
||||
if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize))
|
||||
return Value();
|
||||
@ -105,7 +77,7 @@ namespace DetourNavigator
|
||||
while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize)
|
||||
removeLeastRecentlyUsed();
|
||||
|
||||
const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, std::move(navMeshKey));
|
||||
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)
|
||||
@ -162,8 +134,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();
|
||||
@ -184,7 +156,7 @@ namespace DetourNavigator
|
||||
return;
|
||||
|
||||
mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator);
|
||||
mFreeNavMeshDataSize -= getSize(*iterator);
|
||||
mFreeNavMeshDataSize -= iterator->mSize;
|
||||
}
|
||||
|
||||
void NavMeshTilesCache::releaseItem(ItemIterator iterator)
|
||||
@ -195,71 +167,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;
|
||||
}
|
||||
}
|
||||
|
@ -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,79 +200,9 @@ 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;
|
||||
@ -205,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) + item.mNavMeshKey.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,12 @@ namespace DetourNavigator
|
||||
{
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user