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.hpp

155 lines
5.0 KiB
C++

#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H
#include "navmeshcacheitem.hpp"
#include "offmeshconnectionsmanager.hpp"
#include "tilecachedrecastmeshmanager.hpp"
#include "tileposition.hpp"
#include "navmeshtilescache.hpp"
#include "waitconditiontype.hpp"
#include <osg/Vec3f>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <deque>
#include <set>
#include <thread>
#include <tuple>
class dtNavMesh;
namespace Loading
{
class Listener;
}
namespace DetourNavigator
{
enum class ChangeType
{
remove = 0,
mixed = 1,
add = 2,
update = 3,
};
inline std::ostream& operator <<(std::ostream& stream, ChangeType value)
{
switch (value) {
case ChangeType::remove:
return stream << "ChangeType::remove";
case ChangeType::mixed:
return stream << "ChangeType::mixed";
case ChangeType::add:
return stream << "ChangeType::add";
case ChangeType::update:
return stream << "ChangeType::update";
}
return stream << "ChangeType::" << static_cast<int>(value);
}
class AsyncNavMeshUpdater
{
public:
AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
OffMeshConnectionsManager& offMeshConnectionsManager);
~AsyncNavMeshUpdater();
void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem,
const TilePosition& playerTile, const std::map<TilePosition, ChangeType>& changedTiles);
void wait(Loading::Listener& listener, WaitConditionType waitConditionType);
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
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<TileCachedRecastMeshManager> mRecastMeshManager;
std::reference_wrapper<OffMeshConnectionsManager> mOffMeshConnectionsManager;
std::atomic_bool mShouldStop;
mutable std::mutex mMutex;
std::condition_variable mHasJob;
std::condition_variable mDone;
std::condition_variable mProcessed;
Jobs mJobs;
std::map<osg::Vec3f, std::set<TilePosition>> mPushed;
Misc::ScopeGuarded<TilePosition> mPlayerTile;
Misc::ScopeGuarded<std::optional<std::chrono::steady_clock::time_point>> mFirstStart;
NavMeshTilesCache mNavMeshTilesCache;
Misc::ScopeGuarded<std::map<osg::Vec3f, std::map<TilePosition, std::thread::id>>> mProcessingTiles;
std::map<osg::Vec3f, std::map<TilePosition, std::chrono::steady_clock::time_point>> mLastUpdates;
std::set<std::tuple<osg::Vec3f, TilePosition>> mPresentTiles;
std::map<std::thread::id, Queue> mThreadsQueues;
std::vector<std::thread> mThreads;
void process() noexcept;
bool processJob(const Job& job);
std::optional<Job> getNextJob();
std::optional<Job> getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate);
void postThreadJob(Job&& job, Queue& queue);
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(Job&& job);
std::thread::id lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);
void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile);
inline std::size_t getTotalJobs() const;
inline std::size_t getTotalThreadJobsUnsafe() const;
void cleanupLastUpdates();
int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener);
void waitUntilAllJobsDone();
};
}
#endif