You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/components/detournavigator/asyncnavmeshupdater.cpp

119 lines
3.7 KiB
C++

#include "asyncnavmeshupdater.hpp"
#include "debug.hpp"
#include "makenavmesh.hpp"
#include "settings.hpp"
#include <components/debug/debuglog.hpp>
#include <iostream>
namespace DetourNavigator
{
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings)
: mSettings(std::cref(settings))
, mShouldStop()
, mThread([&] { process(); })
{
}
AsyncNavMeshUpdater::~AsyncNavMeshUpdater()
{
mShouldStop = true;
std::unique_lock<std::mutex> lock(mMutex);
mJobs.clear();
mHasJob.notify_all();
lock.unlock();
mThread.join();
}
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr<RecastMesh>& recastMesh,
const std::shared_ptr<NavMeshCacheItem>& navMeshCacheItem, std::set<TilePosition>&& changedTiles)
{
const std::lock_guard<std::mutex> lock(mMutex);
const auto job = mJobs.find(agentHalfExtents);
if (job == mJobs.end() || job->second.mChangedTiles.empty())
{
mJobs[agentHalfExtents] = Job {agentHalfExtents, recastMesh, navMeshCacheItem, std::move(changedTiles)};
}
else
{
job->second.mRecastMesh = recastMesh;
job->second.mChangedTiles.insert(changedTiles.begin(), changedTiles.end());
}
mHasJob.notify_all();
}
void AsyncNavMeshUpdater::wait()
{
std::unique_lock<std::mutex> lock(mMutex);
mDone.wait(lock, [&] { return mJobs.empty(); });
}
void AsyncNavMeshUpdater::process() throw()
{
log("start process jobs");
while (!mShouldStop)
{
try
{
if (const auto job = getNextJob())
processJob(*job);
}
catch (const std::exception& e)
{
DetourNavigator::log("AsyncNavMeshUpdater::process exception: ", e.what());
::Log(Debug::Error) << "Exception while process navmesh updated job: " << e.what();
}
}
log("stop process jobs");
}
void AsyncNavMeshUpdater::processJob(const Job& job)
{
log("process job for agent=", job.mAgentHalfExtents);
const auto start = std::chrono::steady_clock::now();
if (job.mNavMeshCacheItem->mValue && !job.mChangedTiles.empty())
updateNavMesh(job.mAgentHalfExtents, *job.mRecastMesh, job.mChangedTiles, mSettings,
*job.mNavMeshCacheItem->mValue);
else
job.mNavMeshCacheItem->mValue = makeNavMesh(job.mAgentHalfExtents, *job.mRecastMesh, mSettings);
const auto finish = std::chrono::steady_clock::now();
writeDebugFiles(job);
using FloatMs = std::chrono::duration<float, std::milli>;
log("cache updated for agent=", job.mAgentHalfExtents,
" time=", std::chrono::duration_cast<FloatMs>(finish - start).count(), "ms");
}
boost::optional<AsyncNavMeshUpdater::Job> AsyncNavMeshUpdater::getNextJob()
{
std::unique_lock<std::mutex> lock(mMutex);
if (mJobs.empty())
mHasJob.wait_for(lock, std::chrono::milliseconds(10));
if (mJobs.empty())
{
mDone.notify_all();
return boost::none;
}
log("got ", mJobs.size(), " jobs");
const auto job = mJobs.begin()->second;
mJobs.erase(mJobs.begin());
return job;
}
void AsyncNavMeshUpdater::writeDebugFiles(const Job& job) const
{
#ifdef OPENMW_WRITE_TO_FILE
const auto revision = std::to_string((std::chrono::steady_clock::now()
- std::chrono::steady_clock::time_point()).count());
writeToFile(*job.mRecastMesh, revision);
writeToFile(*job.mNavMeshCacheItem->mValue, revision);
#endif
}
}