#include "navmeshtilescache.hpp" #include "stats.hpp" #include namespace DetourNavigator { NavMeshTilesCache::NavMeshTilesCache(const std::size_t maxNavMeshDataSize) : mMaxNavMeshDataSize(maxNavMeshDataSize) , mUsedNavMeshDataSize(0) , mFreeNavMeshDataSize(0) , mHitCount(0) , mGetCount(0) { } NavMeshTilesCache::Value NavMeshTilesCache::get( const AgentBounds& agentBounds, const TilePosition& changedTile, const RecastMesh& recastMesh) { const std::lock_guard lock(mMutex); ++mGetCount; const auto tile = mValues.find(std::tie(agentBounds, changedTile, recastMesh)); if (tile == mValues.end()) return Value(); acquireItemUnsafe(tile->second); ++mHitCount; return Value(*this, tile->second); } NavMeshTilesCache::Value NavMeshTilesCache::set(const AgentBounds& agentBounds, const TilePosition& changedTile, const RecastMesh& recastMesh, std::unique_ptr&& value) { const auto itemSize = sizeof(RecastMesh) + getSize(recastMesh) + (value == nullptr ? 0 : sizeof(PreparedNavMeshData) + getSize(*value)); const std::lock_guard lock(mMutex); if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) return Value(); while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) removeLeastRecentlyUsed(); RecastMeshData key{ recastMesh.getMesh(), recastMesh.getWater(), recastMesh.getHeightfields(), recastMesh.getFlatHeightfields() }; const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentBounds, changedTile, std::move(key), itemSize); const auto emplaced = mValues.emplace( std::make_tuple(agentBounds, changedTile, std::cref(iterator->mRecastMeshData)), iterator); if (!emplaced.second) { mFreeItems.erase(iterator); acquireItemUnsafe(emplaced.first->second); ++mGetCount; ++mHitCount; return Value(*this, emplaced.first->second); } iterator->mPreparedNavMeshData = std::move(value); ++iterator->mUseCount; mUsedNavMeshDataSize += itemSize; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); return Value(*this, iterator); } NavMeshTilesCacheStats NavMeshTilesCache::getStats() const { NavMeshTilesCacheStats result; { const std::lock_guard lock(mMutex); result.mNavMeshCacheSize = mUsedNavMeshDataSize; result.mUsedNavMeshTiles = mBusyItems.size(); result.mCachedNavMeshTiles = mFreeItems.size(); result.mHitCount = mHitCount; result.mGetCount = mGetCount; } return result; } void NavMeshTilesCache::removeLeastRecentlyUsed() { const auto& item = mFreeItems.back(); const auto value = mValues.find(std::tie(item.mAgentBounds, item.mChangedTile, item.mRecastMeshData)); if (value == mValues.end()) return; mUsedNavMeshDataSize -= item.mSize; mFreeNavMeshDataSize -= item.mSize; mValues.erase(value); mFreeItems.pop_back(); } void NavMeshTilesCache::acquireItemUnsafe(ItemIterator iterator) { if (++iterator->mUseCount > 1) return; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); mFreeNavMeshDataSize -= iterator->mSize; } void NavMeshTilesCache::releaseItem(ItemIterator iterator) { if (--iterator->mUseCount > 0) return; const std::lock_guard lock(mMutex); mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator); mFreeNavMeshDataSize += iterator->mSize; } }