mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-13 11:13:06 +00:00
Merge branch 'navmesh_job' into 'master'
Store async navmesh jobs in the same container until they are processed See merge request OpenMW/openmw!1131
This commit is contained in:
commit
6360bdc859
3 changed files with 167 additions and 200 deletions
|
|
@ -20,6 +20,8 @@ namespace
|
||||||
using DetourNavigator::TilePosition;
|
using DetourNavigator::TilePosition;
|
||||||
using DetourNavigator::UpdateType;
|
using DetourNavigator::UpdateType;
|
||||||
using DetourNavigator::ChangeType;
|
using DetourNavigator::ChangeType;
|
||||||
|
using DetourNavigator::Job;
|
||||||
|
using DetourNavigator::JobIt;
|
||||||
|
|
||||||
int getManhattanDistance(const TilePosition& lhs, const TilePosition& rhs)
|
int getManhattanDistance(const TilePosition& lhs, const TilePosition& rhs)
|
||||||
{
|
{
|
||||||
|
|
@ -27,13 +29,12 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
int getMinDistanceTo(const TilePosition& position, int maxDistance,
|
int getMinDistanceTo(const TilePosition& position, int maxDistance,
|
||||||
const std::map<osg::Vec3f, std::set<TilePosition>>& tilesPerHalfExtents,
|
const std::set<std::tuple<osg::Vec3f, TilePosition>>& pushedTiles,
|
||||||
const std::set<std::tuple<osg::Vec3f, TilePosition>>& presentTiles)
|
const std::set<std::tuple<osg::Vec3f, TilePosition>>& presentTiles)
|
||||||
{
|
{
|
||||||
int result = maxDistance;
|
int result = maxDistance;
|
||||||
for (const auto& [halfExtents, tiles] : tilesPerHalfExtents)
|
for (const auto& [halfExtents, tile] : pushedTiles)
|
||||||
for (const TilePosition& tile : tiles)
|
if (presentTiles.find(std::tie(halfExtents, tile)) == presentTiles.end())
|
||||||
if (presentTiles.find(std::make_tuple(halfExtents, tile)) == presentTiles.end())
|
|
||||||
result = std::min(result, getManhattanDistance(position, tile));
|
result = std::min(result, getManhattanDistance(position, tile));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -44,34 +45,45 @@ namespace
|
||||||
return UpdateType::Temporary;
|
return UpdateType::Temporary;
|
||||||
return UpdateType::Persistent;
|
return UpdateType::Persistent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto getPriority(const Job& job) noexcept
|
||||||
|
{
|
||||||
|
return std::make_tuple(job.mProcessTime, job.mTryNumber, job.mChangeType, job.mDistanceToPlayer, job.mDistanceToOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LessByJobPriority
|
||||||
|
{
|
||||||
|
bool operator()(JobIt lhs, JobIt rhs) const noexcept
|
||||||
|
{
|
||||||
|
return getPriority(*lhs) < getPriority(*rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void insertPrioritizedJob(JobIt job, std::deque<JobIt>& queue)
|
||||||
|
{
|
||||||
|
const auto it = std::upper_bound(queue.begin(), queue.end(), job, LessByJobPriority {});
|
||||||
|
queue.insert(it, job);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getAgentAndTile(const Job& job) noexcept
|
||||||
|
{
|
||||||
|
return std::make_tuple(job.mAgentHalfExtents, job.mChangedTile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
static std::ostream& operator <<(std::ostream& stream, UpdateNavMeshStatus value)
|
Job::Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
|
||||||
|
const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
|
||||||
|
std::chrono::steady_clock::time_point processTime)
|
||||||
|
: mAgentHalfExtents(agentHalfExtents)
|
||||||
|
, mNavMeshCacheItem(std::move(navMeshCacheItem))
|
||||||
|
, mChangedTile(changedTile)
|
||||||
|
, mProcessTime(processTime)
|
||||||
|
, mChangeType(changeType)
|
||||||
|
, mDistanceToPlayer(distanceToPlayer)
|
||||||
|
, mDistanceToOrigin(getManhattanDistance(changedTile, TilePosition {0, 0}))
|
||||||
{
|
{
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case UpdateNavMeshStatus::ignored:
|
|
||||||
return stream << "ignore";
|
|
||||||
case UpdateNavMeshStatus::removed:
|
|
||||||
return stream << "removed";
|
|
||||||
case UpdateNavMeshStatus::added:
|
|
||||||
return stream << "add";
|
|
||||||
case UpdateNavMeshStatus::replaced:
|
|
||||||
return stream << "replaced";
|
|
||||||
case UpdateNavMeshStatus::failed:
|
|
||||||
return stream << "failed";
|
|
||||||
case UpdateNavMeshStatus::lost:
|
|
||||||
return stream << "lost";
|
|
||||||
case UpdateNavMeshStatus::cached:
|
|
||||||
return stream << "cached";
|
|
||||||
case UpdateNavMeshStatus::unchanged:
|
|
||||||
return stream << "unchanged";
|
|
||||||
case UpdateNavMeshStatus::restored:
|
|
||||||
return stream << "restored";
|
|
||||||
}
|
|
||||||
return stream << "unknown(" << static_cast<unsigned>(value) << ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
|
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
|
||||||
|
|
@ -90,7 +102,8 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
mShouldStop = true;
|
mShouldStop = true;
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
mJobs = decltype(mJobs)();
|
mThreadsQueues.clear();
|
||||||
|
mWaiting.clear();
|
||||||
mHasJob.notify_all();
|
mHasJob.notify_all();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
for (auto& thread : mThreads)
|
for (auto& thread : mThreads)
|
||||||
|
|
@ -114,44 +127,33 @@ namespace DetourNavigator
|
||||||
const std::lock_guard<std::mutex> lock(mMutex);
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
if (playerTileChanged)
|
if (playerTileChanged)
|
||||||
for (auto& job : mJobs)
|
for (JobIt job : mWaiting)
|
||||||
job.mDistanceToPlayer = getManhattanDistance(job.mChangedTile, playerTile);
|
job->mDistanceToPlayer = getManhattanDistance(job->mChangedTile, playerTile);
|
||||||
|
|
||||||
for (const auto& changedTile : changedTiles)
|
for (const auto& [changedTile, changeType] : changedTiles)
|
||||||
{
|
{
|
||||||
if (mPushed[agentHalfExtents].insert(changedTile.first).second)
|
if (mPushed.emplace(agentHalfExtents, changedTile).second)
|
||||||
{
|
{
|
||||||
Job job;
|
const auto processTime = changeType == ChangeType::update
|
||||||
|
? mLastUpdates[std::tie(agentHalfExtents, changedTile)] + mSettings.get().mMinUpdateInterval
|
||||||
job.mAgentHalfExtents = agentHalfExtents;
|
|
||||||
job.mNavMeshCacheItem = navMeshCacheItem;
|
|
||||||
job.mChangedTile = changedTile.first;
|
|
||||||
job.mTryNumber = 0;
|
|
||||||
job.mChangeType = changedTile.second;
|
|
||||||
job.mDistanceToPlayer = getManhattanDistance(changedTile.first, playerTile);
|
|
||||||
job.mDistanceToOrigin = getManhattanDistance(changedTile.first, TilePosition {0, 0});
|
|
||||||
job.mProcessTime = job.mChangeType == ChangeType::update
|
|
||||||
? mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] + mSettings.get().mMinUpdateInterval
|
|
||||||
: std::chrono::steady_clock::time_point();
|
: std::chrono::steady_clock::time_point();
|
||||||
|
|
||||||
|
const JobIt it = mJobs.emplace(mJobs.end(), agentHalfExtents, navMeshCacheItem, changedTile,
|
||||||
|
changeType, getManhattanDistance(changedTile, playerTile), processTime);
|
||||||
|
|
||||||
if (playerTileChanged)
|
if (playerTileChanged)
|
||||||
{
|
mWaiting.push_back(it);
|
||||||
mJobs.push_back(std::move(job));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
insertPrioritizedJob(it, mWaiting);
|
||||||
const auto it = std::upper_bound(mJobs.begin(), mJobs.end(), job);
|
|
||||||
mJobs.insert(it, std::move(job));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerTileChanged)
|
if (playerTileChanged)
|
||||||
std::sort(mJobs.begin(), mJobs.end());
|
std::sort(mWaiting.begin(), mWaiting.end(), LessByJobPriority {});
|
||||||
|
|
||||||
Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs";
|
Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs";
|
||||||
|
|
||||||
if (!mJobs.empty())
|
if (!mWaiting.empty())
|
||||||
mHasJob.notify_all();
|
mHasJob.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,15 +194,13 @@ namespace DetourNavigator
|
||||||
int minDistanceToPlayer = 0;
|
int minDistanceToPlayer = 0;
|
||||||
const auto isDone = [&]
|
const auto isDone = [&]
|
||||||
{
|
{
|
||||||
jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe();
|
jobsLeft = mJobs.size();
|
||||||
if (jobsLeft == 0)
|
if (jobsLeft == 0)
|
||||||
{
|
{
|
||||||
minDistanceToPlayer = 0;
|
minDistanceToPlayer = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles);
|
minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles);
|
||||||
for (const auto& [threadId, queue] : mThreadsQueues)
|
|
||||||
minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed, mPresentTiles);
|
|
||||||
return minDistanceToPlayer >= maxDistanceToPlayer;
|
return minDistanceToPlayer >= maxDistanceToPlayer;
|
||||||
};
|
};
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
@ -227,7 +227,7 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
mDone.wait(lock, [this] { return mJobs.size() + getTotalThreadJobsUnsafe() == 0; });
|
mDone.wait(lock, [this] { return mJobs.size() == 0; });
|
||||||
}
|
}
|
||||||
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
|
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +238,7 @@ namespace DetourNavigator
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> lock(mMutex);
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
jobs = mJobs.size() + getTotalThreadJobsUnsafe();
|
jobs = mJobs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.setAttribute(frameNumber, "NavMesh UpdateJobs", jobs);
|
stats.setAttribute(frameNumber, "NavMesh UpdateJobs", jobs);
|
||||||
|
|
@ -254,12 +254,14 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (auto job = getNextJob())
|
if (JobIt job = getNextJob(); job != mJobs.end())
|
||||||
{
|
{
|
||||||
const auto processed = processJob(*job);
|
const auto processed = processJob(*job);
|
||||||
unlockTile(job->mAgentHalfExtents, job->mChangedTile);
|
unlockTile(job->mAgentHalfExtents, job->mChangedTile);
|
||||||
if (!processed)
|
if (processed)
|
||||||
repost(std::move(*job));
|
removeJob(job);
|
||||||
|
else
|
||||||
|
repost(job);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cleanupLastUpdates();
|
cleanupLastUpdates();
|
||||||
|
|
@ -279,8 +281,6 @@ namespace DetourNavigator
|
||||||
|
|
||||||
const auto start = std::chrono::steady_clock::now();
|
const auto start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
const auto firstStart = setFirstStart(start);
|
|
||||||
|
|
||||||
const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();
|
const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();
|
||||||
|
|
||||||
if (!navMeshCacheItem)
|
if (!navMeshCacheItem)
|
||||||
|
|
@ -331,13 +331,12 @@ namespace DetourNavigator
|
||||||
" generation=" << locked->getGeneration() <<
|
" generation=" << locked->getGeneration() <<
|
||||||
" revision=" << locked->getNavMeshRevision() <<
|
" revision=" << locked->getNavMeshRevision() <<
|
||||||
" time=" << std::chrono::duration_cast<FloatMs>(finish - start).count() << "ms" <<
|
" time=" << std::chrono::duration_cast<FloatMs>(finish - start).count() << "ms" <<
|
||||||
" total_time=" << std::chrono::duration_cast<FloatMs>(finish - firstStart).count() << "ms"
|
|
||||||
" thread=" << std::this_thread::get_id();
|
" thread=" << std::this_thread::get_id();
|
||||||
|
|
||||||
return isSuccess(status);
|
return isSuccess(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
|
JobIt AsyncNavMeshUpdater::getNextJob()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
|
@ -347,54 +346,51 @@ namespace DetourNavigator
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
const auto hasJob = [&] {
|
const auto hasJob = [&] {
|
||||||
return (!mJobs.empty() && mJobs.front().mProcessTime <= std::chrono::steady_clock::now())
|
return (!mWaiting.empty() && mWaiting.front()->mProcessTime <= std::chrono::steady_clock::now())
|
||||||
|| !threadQueue.mJobs.empty();
|
|| !threadQueue.empty();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob))
|
if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob))
|
||||||
{
|
{
|
||||||
mFirstStart.lock()->reset();
|
if (mJobs.empty())
|
||||||
if (mJobs.empty() && getTotalThreadJobsUnsafe() == 0)
|
|
||||||
mDone.notify_all();
|
mDone.notify_all();
|
||||||
return std::nullopt;
|
return mJobs.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(Debug::Debug) << "Got " << mJobs.size() << " navigator jobs and "
|
Log(Debug::Debug) << "Got " << mJobs.size() << " navigator jobs and "
|
||||||
<< threadQueue.mJobs.size() << " thread jobs by thread=" << std::this_thread::get_id();
|
<< threadQueue.size() << " thread jobs by thread=" << std::this_thread::get_id();
|
||||||
|
|
||||||
auto job = threadQueue.mJobs.empty()
|
const JobIt job = threadQueue.empty()
|
||||||
? getJob(mJobs, mPushed, true)
|
? getJob(mWaiting, true)
|
||||||
: getJob(threadQueue.mJobs, threadQueue.mPushed, false);
|
: getJob(threadQueue, false);
|
||||||
|
|
||||||
if (!job)
|
if (job == mJobs.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto owner = lockTile(job->mAgentHalfExtents, job->mChangedTile);
|
const auto owner = lockTile(job->mAgentHalfExtents, job->mChangedTile);
|
||||||
|
|
||||||
if (owner == threadId)
|
if (owner == threadId)
|
||||||
|
{
|
||||||
|
mPushed.erase(getAgentAndTile(*job));
|
||||||
return job;
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
postThreadJob(std::move(*job), mThreadsQueues[owner]);
|
postThreadJob(job, mThreadsQueues[owner]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate)
|
JobIt AsyncNavMeshUpdater::getJob(std::deque<JobIt>& jobs, bool changeLastUpdate)
|
||||||
{
|
{
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
JobIt job = jobs.front();
|
||||||
|
|
||||||
if (jobs.front().mProcessTime > now)
|
if (job->mProcessTime > now)
|
||||||
return {};
|
return mJobs.end();
|
||||||
|
|
||||||
Job job = jobs.front();
|
|
||||||
jobs.pop_front();
|
jobs.pop_front();
|
||||||
|
|
||||||
if (changeLastUpdate && job.mChangeType == ChangeType::update)
|
if (changeLastUpdate && job->mChangeType == ChangeType::update)
|
||||||
mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] = now;
|
mLastUpdates[getAgentAndTile(*job)] = now;
|
||||||
|
|
||||||
const auto it = pushed.find(job.mAgentHalfExtents);
|
|
||||||
it->second.erase(job.mChangedTile);
|
|
||||||
if (it->second.empty())
|
|
||||||
pushed.erase(it);
|
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
@ -422,36 +418,28 @@ namespace DetourNavigator
|
||||||
writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
|
writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point AsyncNavMeshUpdater::setFirstStart(const std::chrono::steady_clock::time_point& value)
|
void AsyncNavMeshUpdater::repost(JobIt job)
|
||||||
{
|
{
|
||||||
const auto locked = mFirstStart.lock();
|
if (mShouldStop || job->mTryNumber > 2)
|
||||||
if (!*locked)
|
|
||||||
*locked = value;
|
|
||||||
return *locked.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncNavMeshUpdater::repost(Job&& job)
|
|
||||||
{
|
|
||||||
if (mShouldStop || job.mTryNumber > 2)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const std::lock_guard<std::mutex> lock(mMutex);
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
if (mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second)
|
if (mPushed.emplace(job->mAgentHalfExtents, job->mChangedTile).second)
|
||||||
{
|
{
|
||||||
++job.mTryNumber;
|
++job->mTryNumber;
|
||||||
mJobs.push_back(std::move(job));
|
mWaiting.push_back(job);
|
||||||
mHasJob.notify_all();
|
mHasJob.notify_all();
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncNavMeshUpdater::postThreadJob(Job&& job, Queue& queue)
|
mJobs.erase(job);
|
||||||
{
|
|
||||||
if (queue.mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second)
|
|
||||||
{
|
|
||||||
queue.mJobs.push_back(std::move(job));
|
|
||||||
mHasJob.notify_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncNavMeshUpdater::postThreadJob(JobIt job, std::deque<JobIt>& queue)
|
||||||
|
{
|
||||||
|
queue.push_back(job);
|
||||||
|
mHasJob.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread::id AsyncNavMeshUpdater::lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile)
|
std::thread::id AsyncNavMeshUpdater::lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile)
|
||||||
|
|
@ -460,23 +448,13 @@ namespace DetourNavigator
|
||||||
return std::this_thread::get_id();
|
return std::this_thread::get_id();
|
||||||
|
|
||||||
auto locked = mProcessingTiles.lock();
|
auto locked = mProcessingTiles.lock();
|
||||||
|
const auto tile = locked->find(std::make_tuple(agentHalfExtents, changedTile));
|
||||||
auto agent = locked->find(agentHalfExtents);
|
if (tile == locked->end())
|
||||||
if (agent == locked->end())
|
|
||||||
{
|
{
|
||||||
const auto threadId = std::this_thread::get_id();
|
const auto threadId = std::this_thread::get_id();
|
||||||
locked->emplace(agentHalfExtents, std::map<TilePosition, std::thread::id>({{changedTile, threadId}}));
|
locked->emplace(std::tie(agentHalfExtents, changedTile), threadId);
|
||||||
return threadId;
|
return threadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tile = agent->second.find(changedTile);
|
|
||||||
if (tile == agent->second.end())
|
|
||||||
{
|
|
||||||
const auto threadId = std::this_thread::get_id();
|
|
||||||
agent->second.emplace(changedTile, threadId);
|
|
||||||
return threadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tile->second;
|
return tile->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -484,22 +462,8 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
if (mSettings.get().mAsyncNavMeshUpdaterThreads <= 1)
|
if (mSettings.get().mAsyncNavMeshUpdaterThreads <= 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto locked = mProcessingTiles.lock();
|
auto locked = mProcessingTiles.lock();
|
||||||
|
locked->erase(std::tie(agentHalfExtents, changedTile));
|
||||||
auto agent = locked->find(agentHalfExtents);
|
|
||||||
if (agent == locked->end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto tile = agent->second.find(changedTile);
|
|
||||||
if (tile == agent->second.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
agent->second.erase(tile);
|
|
||||||
|
|
||||||
if (agent->second.empty())
|
|
||||||
locked->erase(agent);
|
|
||||||
|
|
||||||
if (locked->empty())
|
if (locked->empty())
|
||||||
mProcessed.notify_all();
|
mProcessed.notify_all();
|
||||||
}
|
}
|
||||||
|
|
@ -507,13 +471,7 @@ namespace DetourNavigator
|
||||||
std::size_t AsyncNavMeshUpdater::getTotalJobs() const
|
std::size_t AsyncNavMeshUpdater::getTotalJobs() const
|
||||||
{
|
{
|
||||||
const std::scoped_lock lock(mMutex);
|
const std::scoped_lock lock(mMutex);
|
||||||
return mJobs.size() + getTotalThreadJobsUnsafe();
|
return mJobs.size();
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const
|
|
||||||
{
|
|
||||||
return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0),
|
|
||||||
[] (auto r, const auto& v) { return r + v.second.mJobs.size(); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncNavMeshUpdater::cleanupLastUpdates()
|
void AsyncNavMeshUpdater::cleanupLastUpdates()
|
||||||
|
|
@ -522,20 +480,18 @@ namespace DetourNavigator
|
||||||
|
|
||||||
const std::lock_guard<std::mutex> lock(mMutex);
|
const std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
for (auto agent = mLastUpdates.begin(); agent != mLastUpdates.end();)
|
for (auto it = mLastUpdates.begin(); it != mLastUpdates.end();)
|
||||||
{
|
{
|
||||||
for (auto tile = agent->second.begin(); tile != agent->second.end();)
|
if (now - it->second > mSettings.get().mMinUpdateInterval)
|
||||||
{
|
it = mLastUpdates.erase(it);
|
||||||
if (now - tile->second > mSettings.get().mMinUpdateInterval)
|
|
||||||
tile = agent->second.erase(tile);
|
|
||||||
else
|
else
|
||||||
++tile;
|
++it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agent->second.empty())
|
void AsyncNavMeshUpdater::removeJob(JobIt job)
|
||||||
agent = mLastUpdates.erase(agent);
|
{
|
||||||
else
|
const std::lock_guard lock(mMutex);
|
||||||
++agent;
|
mJobs.erase(job);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
class dtNavMesh;
|
class dtNavMesh;
|
||||||
|
|
||||||
|
|
@ -52,6 +53,24 @@ namespace DetourNavigator
|
||||||
return stream << "ChangeType::" << static_cast<int>(value);
|
return stream << "ChangeType::" << static_cast<int>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Job
|
||||||
|
{
|
||||||
|
const osg::Vec3f mAgentHalfExtents;
|
||||||
|
const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
|
||||||
|
const TilePosition mChangedTile;
|
||||||
|
const std::chrono::steady_clock::time_point mProcessTime;
|
||||||
|
unsigned mTryNumber = 0;
|
||||||
|
const ChangeType mChangeType;
|
||||||
|
int mDistanceToPlayer;
|
||||||
|
const int mDistanceToOrigin;
|
||||||
|
|
||||||
|
Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
|
||||||
|
const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
|
||||||
|
std::chrono::steady_clock::time_point processTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
using JobIt = std::list<Job>::iterator;
|
||||||
|
|
||||||
class AsyncNavMeshUpdater
|
class AsyncNavMeshUpdater
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -67,39 +86,6 @@ namespace DetourNavigator
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Job
|
|
||||||
{
|
|
||||||
osg::Vec3f mAgentHalfExtents;
|
|
||||||
std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
|
|
||||||
TilePosition mChangedTile;
|
|
||||||
unsigned mTryNumber;
|
|
||||||
ChangeType mChangeType;
|
|
||||||
int mDistanceToPlayer;
|
|
||||||
int mDistanceToOrigin;
|
|
||||||
std::chrono::steady_clock::time_point mProcessTime;
|
|
||||||
|
|
||||||
std::tuple<std::chrono::steady_clock::time_point, unsigned, ChangeType, int, int> getPriority() const
|
|
||||||
{
|
|
||||||
return std::make_tuple(mProcessTime, mTryNumber, mChangeType, mDistanceToPlayer, mDistanceToOrigin);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend inline bool operator <(const Job& lhs, const Job& rhs)
|
|
||||||
{
|
|
||||||
return lhs.getPriority() < rhs.getPriority();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using Jobs = std::deque<Job>;
|
|
||||||
using Pushed = std::map<osg::Vec3f, std::set<TilePosition>>;
|
|
||||||
|
|
||||||
struct Queue
|
|
||||||
{
|
|
||||||
Jobs mJobs;
|
|
||||||
Pushed mPushed;
|
|
||||||
|
|
||||||
Queue() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::reference_wrapper<const Settings> mSettings;
|
std::reference_wrapper<const Settings> mSettings;
|
||||||
std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;
|
std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;
|
||||||
std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;
|
std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;
|
||||||
|
|
@ -108,32 +94,30 @@ namespace DetourNavigator
|
||||||
std::condition_variable mHasJob;
|
std::condition_variable mHasJob;
|
||||||
std::condition_variable mDone;
|
std::condition_variable mDone;
|
||||||
std::condition_variable mProcessed;
|
std::condition_variable mProcessed;
|
||||||
Jobs mJobs;
|
std::list<Job> mJobs;
|
||||||
std::map<osg::Vec3f, std::set<TilePosition>> mPushed;
|
std::deque<JobIt> mWaiting;
|
||||||
|
std::set<std::tuple<osg::Vec3f, TilePosition>> mPushed;
|
||||||
Misc::ScopeGuarded<TilePosition> mPlayerTile;
|
Misc::ScopeGuarded<TilePosition> mPlayerTile;
|
||||||
Misc::ScopeGuarded<std::optional<std::chrono::steady_clock::time_point>> mFirstStart;
|
|
||||||
NavMeshTilesCache mNavMeshTilesCache;
|
NavMeshTilesCache mNavMeshTilesCache;
|
||||||
Misc::ScopeGuarded<std::map<osg::Vec3f, std::map<TilePosition, std::thread::id>>> mProcessingTiles;
|
Misc::ScopeGuarded<std::map<std::tuple<osg::Vec3f, TilePosition>, std::thread::id>> mProcessingTiles;
|
||||||
std::map<osg::Vec3f, std::map<TilePosition, std::chrono::steady_clock::time_point>> mLastUpdates;
|
std::map<std::tuple<osg::Vec3f, TilePosition>, std::chrono::steady_clock::time_point> mLastUpdates;
|
||||||
std::set<std::tuple<osg::Vec3f, TilePosition>> mPresentTiles;
|
std::set<std::tuple<osg::Vec3f, TilePosition>> mPresentTiles;
|
||||||
std::map<std::thread::id, Queue> mThreadsQueues;
|
std::map<std::thread::id, std::deque<JobIt>> mThreadsQueues;
|
||||||
std::vector<std::thread> mThreads;
|
std::vector<std::thread> mThreads;
|
||||||
|
|
||||||
void process() noexcept;
|
void process() noexcept;
|
||||||
|
|
||||||
bool processJob(const Job& job);
|
bool processJob(const Job& job);
|
||||||
|
|
||||||
std::optional<Job> getNextJob();
|
JobIt getNextJob();
|
||||||
|
|
||||||
std::optional<Job> getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate);
|
JobIt getJob(std::deque<JobIt>& jobs, bool changeLastUpdate);
|
||||||
|
|
||||||
void postThreadJob(Job&& job, Queue& queue);
|
void postThreadJob(JobIt job, std::deque<JobIt>& queue);
|
||||||
|
|
||||||
void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const;
|
void writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point setFirstStart(const std::chrono::steady_clock::time_point& value);
|
void repost(JobIt job);
|
||||||
|
|
||||||
void repost(Job&& job);
|
|
||||||
|
|
||||||
std::thread::id lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);
|
std::thread::id lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);
|
||||||
|
|
||||||
|
|
@ -141,13 +125,13 @@ namespace DetourNavigator
|
||||||
|
|
||||||
inline std::size_t getTotalJobs() const;
|
inline std::size_t getTotalJobs() const;
|
||||||
|
|
||||||
inline std::size_t getTotalThreadJobsUnsafe() const;
|
|
||||||
|
|
||||||
void cleanupLastUpdates();
|
void cleanupLastUpdates();
|
||||||
|
|
||||||
int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);
|
int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);
|
||||||
|
|
||||||
void waitUntilAllJobsDone();
|
void waitUntilAllJobsDone();
|
||||||
|
|
||||||
|
inline void removeJob(JobIt job);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <components/misc/guarded.hpp>
|
#include <components/misc/guarded.hpp>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
struct dtMeshTile;
|
struct dtMeshTile;
|
||||||
|
|
||||||
|
|
@ -33,6 +34,32 @@ namespace DetourNavigator
|
||||||
return (static_cast<unsigned>(value) & static_cast<unsigned>(UpdateNavMeshStatus::failed)) == 0;
|
return (static_cast<unsigned>(value) & static_cast<unsigned>(UpdateNavMeshStatus::failed)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator <<(std::ostream& stream, UpdateNavMeshStatus value)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case UpdateNavMeshStatus::ignored:
|
||||||
|
return stream << "ignore";
|
||||||
|
case UpdateNavMeshStatus::removed:
|
||||||
|
return stream << "removed";
|
||||||
|
case UpdateNavMeshStatus::added:
|
||||||
|
return stream << "add";
|
||||||
|
case UpdateNavMeshStatus::replaced:
|
||||||
|
return stream << "replaced";
|
||||||
|
case UpdateNavMeshStatus::failed:
|
||||||
|
return stream << "failed";
|
||||||
|
case UpdateNavMeshStatus::lost:
|
||||||
|
return stream << "lost";
|
||||||
|
case UpdateNavMeshStatus::cached:
|
||||||
|
return stream << "cached";
|
||||||
|
case UpdateNavMeshStatus::unchanged:
|
||||||
|
return stream << "unchanged";
|
||||||
|
case UpdateNavMeshStatus::restored:
|
||||||
|
return stream << "restored";
|
||||||
|
}
|
||||||
|
return stream << "unknown(" << static_cast<unsigned>(value) << ")";
|
||||||
|
}
|
||||||
|
|
||||||
class UpdateNavMeshStatusBuilder
|
class UpdateNavMeshStatusBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue