1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-13 23:13:05 +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:
psi29a 2021-08-17 09:20:54 +00:00
commit 6360bdc859
3 changed files with 167 additions and 200 deletions

View file

@ -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,14 +29,13 @@ 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;
} }
mJobs.erase(job);
} }
void AsyncNavMeshUpdater::postThreadJob(Job&& job, Queue& queue) void AsyncNavMeshUpdater::postThreadJob(JobIt job, std::deque<JobIt>& queue)
{ {
if (queue.mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second) queue.push_back(job);
{ mHasJob.notify_all();
queue.mJobs.push_back(std::move(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
++tile;
}
if (agent->second.empty())
agent = mLastUpdates.erase(agent);
else else
++agent; ++it;
} }
} }
void AsyncNavMeshUpdater::removeJob(JobIt job)
{
const std::lock_guard lock(mMutex);
mJobs.erase(job);
}
} }

View file

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

View file

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