mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 01:56:43 +00:00 
			
		
		
		
	* Do not fail tile generation if debug mesh writing fails. * Mark some functions as noexcept to better crash than have a deadlock. * Unlock tile and remove job if there on exception while processing it.
		
			
				
	
	
		
			265 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H
 | |
| #define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H
 | |
| 
 | |
| #include "agentbounds.hpp"
 | |
| #include "changetype.hpp"
 | |
| #include "guardednavmeshcacheitem.hpp"
 | |
| #include "navmeshcacheitem.hpp"
 | |
| #include "navmeshdb.hpp"
 | |
| #include "navmeshtilescache.hpp"
 | |
| #include "offmeshconnectionsmanager.hpp"
 | |
| #include "sharednavmeshcacheitem.hpp"
 | |
| #include "stats.hpp"
 | |
| #include "tilecachedrecastmeshmanager.hpp"
 | |
| #include "tileposition.hpp"
 | |
| #include "waitconditiontype.hpp"
 | |
| 
 | |
| #include <boost/geometry/geometries/point.hpp>
 | |
| #include <boost/geometry/index/rtree.hpp>
 | |
| 
 | |
| #include <atomic>
 | |
| #include <chrono>
 | |
| #include <condition_variable>
 | |
| #include <deque>
 | |
| #include <iosfwd>
 | |
| #include <list>
 | |
| #include <memory>
 | |
| #include <mutex>
 | |
| #include <optional>
 | |
| #include <set>
 | |
| #include <thread>
 | |
| #include <tuple>
 | |
| 
 | |
| class dtNavMesh;
 | |
| 
 | |
| namespace Loading
 | |
| {
 | |
|     class Listener;
 | |
| }
 | |
| 
 | |
| namespace DetourNavigator
 | |
| {
 | |
|     enum class JobState
 | |
|     {
 | |
|         Initial,
 | |
|         WithDbResult,
 | |
|     };
 | |
| 
 | |
|     struct Job
 | |
|     {
 | |
|         const std::size_t mId;
 | |
|         const AgentBounds mAgentBounds;
 | |
|         const std::weak_ptr<GuardedNavMeshCacheItem> mNavMeshCacheItem;
 | |
|         const ESM::RefId mWorldspace;
 | |
|         const TilePosition mChangedTile;
 | |
|         std::chrono::steady_clock::time_point mProcessTime;
 | |
|         ChangeType mChangeType;
 | |
|         JobState mState = JobState::Initial;
 | |
|         std::vector<std::byte> mInput;
 | |
|         std::shared_ptr<RecastMesh> mRecastMesh;
 | |
|         std::optional<TileData> mCachedTileData;
 | |
|         std::unique_ptr<PreparedNavMeshData> mGeneratedNavMeshData;
 | |
| 
 | |
|         Job(const AgentBounds& agentBounds, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
 | |
|             ESM::RefId worldspace, const TilePosition& changedTile, ChangeType changeType,
 | |
|             std::chrono::steady_clock::time_point processTime);
 | |
|     };
 | |
| 
 | |
|     using JobIt = std::list<Job>::iterator;
 | |
| 
 | |
|     class SpatialJobQueue
 | |
|     {
 | |
|     public:
 | |
|         std::size_t size() const { return mSize; }
 | |
| 
 | |
|         void clear();
 | |
| 
 | |
|         void push(JobIt job);
 | |
| 
 | |
|         std::optional<JobIt> pop(TilePosition playerTile);
 | |
| 
 | |
|         void update(TilePosition playerTile, int maxTiles, std::vector<JobIt>& removing);
 | |
| 
 | |
|     private:
 | |
|         using IndexPoint = boost::geometry::model::point<int, 2, boost::geometry::cs::cartesian>;
 | |
|         using UpdatingMap = std::map<TilePosition, std::deque<JobIt>>;
 | |
|         using IndexValue = std::pair<IndexPoint, UpdatingMap::iterator>;
 | |
| 
 | |
|         std::size_t mSize = 0;
 | |
|         UpdatingMap mValues;
 | |
|         boost::geometry::index::rtree<IndexValue, boost::geometry::index::linear<4>> mIndex;
 | |
|     };
 | |
| 
 | |
|     class JobQueue
 | |
|     {
 | |
|     public:
 | |
|         JobQueueStats getStats() const
 | |
|         {
 | |
|             return JobQueueStats{
 | |
|                 .mRemoving = mRemoving.size(),
 | |
|                 .mUpdating = mUpdating.size(),
 | |
|                 .mDelayed = mDelayed.size(),
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         bool hasJob(std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now()) const;
 | |
| 
 | |
|         void clear();
 | |
| 
 | |
|         void push(JobIt job, std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
 | |
| 
 | |
|         std::optional<JobIt> pop(
 | |
|             TilePosition playerTile, std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
 | |
| 
 | |
|         void update(TilePosition playerTile, int maxTiles,
 | |
|             std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
 | |
| 
 | |
|     private:
 | |
|         std::vector<JobIt> mRemoving;
 | |
|         SpatialJobQueue mUpdating;
 | |
|         std::deque<JobIt> mDelayed;
 | |
|     };
 | |
| 
 | |
|     enum class JobStatus
 | |
|     {
 | |
|         Done,
 | |
|         Fail,
 | |
|         MemoryCacheMiss,
 | |
|     };
 | |
| 
 | |
|     std::ostream& operator<<(std::ostream& stream, JobStatus value);
 | |
| 
 | |
|     class DbJobQueue
 | |
|     {
 | |
|     public:
 | |
|         void push(JobIt job);
 | |
| 
 | |
|         std::optional<JobIt> pop();
 | |
| 
 | |
|         void update(TilePosition playerTile);
 | |
| 
 | |
|         void stop();
 | |
| 
 | |
|         DbJobQueueStats getStats() const;
 | |
| 
 | |
|     private:
 | |
|         mutable std::mutex mMutex;
 | |
|         std::condition_variable mHasJob;
 | |
|         SpatialJobQueue mReading;
 | |
|         std::deque<JobIt> mWriting;
 | |
|         TilePosition mPlayerTile;
 | |
|         bool mShouldStop = false;
 | |
|     };
 | |
| 
 | |
|     class AsyncNavMeshUpdater;
 | |
| 
 | |
|     class DbWorker
 | |
|     {
 | |
|     public:
 | |
|         DbWorker(AsyncNavMeshUpdater& updater, std::unique_ptr<NavMeshDb>&& db, TileVersion version,
 | |
|             const RecastSettings& recastSettings, bool writeToDb);
 | |
| 
 | |
|         ~DbWorker();
 | |
| 
 | |
|         DbWorkerStats getStats() const;
 | |
| 
 | |
|         void enqueueJob(JobIt job);
 | |
| 
 | |
|         void update(TilePosition playerTile) { mQueue.update(playerTile); }
 | |
| 
 | |
|         void stop();
 | |
| 
 | |
|     private:
 | |
|         AsyncNavMeshUpdater& mUpdater;
 | |
|         const RecastSettings& mRecastSettings;
 | |
|         const std::unique_ptr<NavMeshDb> mDb;
 | |
|         const TileVersion mVersion;
 | |
|         bool mWriteToDb;
 | |
|         TileId mNextTileId;
 | |
|         ShapeId mNextShapeId;
 | |
|         DbJobQueue mQueue;
 | |
|         std::atomic_bool mShouldStop{ false };
 | |
|         std::atomic_size_t mGetTileCount{ 0 };
 | |
|         std::thread mThread;
 | |
| 
 | |
|         inline void run() noexcept;
 | |
| 
 | |
|         inline void processJob(JobIt job);
 | |
| 
 | |
|         inline void processReadingJob(JobIt job);
 | |
| 
 | |
|         inline void processWritingJob(JobIt job);
 | |
|     };
 | |
| 
 | |
|     class AsyncNavMeshUpdater
 | |
|     {
 | |
|     public:
 | |
|         AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
 | |
|             OffMeshConnectionsManager& offMeshConnectionsManager, std::unique_ptr<NavMeshDb>&& db);
 | |
|         ~AsyncNavMeshUpdater();
 | |
| 
 | |
|         void post(const AgentBounds& agentBounds, const SharedNavMeshCacheItem& navMeshCacheItem,
 | |
|             const TilePosition& playerTile, ESM::RefId worldspace,
 | |
|             const std::map<TilePosition, ChangeType>& changedTiles);
 | |
| 
 | |
|         void wait(WaitConditionType waitConditionType, Loading::Listener* listener);
 | |
| 
 | |
|         void stop();
 | |
| 
 | |
|         AsyncNavMeshUpdaterStats getStats() const;
 | |
| 
 | |
|         void enqueueJob(JobIt job);
 | |
| 
 | |
|         void removeJob(JobIt job);
 | |
| 
 | |
|     private:
 | |
|         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;
 | |
|         std::list<Job> mJobs;
 | |
|         JobQueue mWaiting;
 | |
|         std::set<std::tuple<AgentBounds, TilePosition>> mPushed;
 | |
|         Misc::ScopeGuarded<TilePosition> mPlayerTile;
 | |
|         NavMeshTilesCache mNavMeshTilesCache;
 | |
|         Misc::ScopeGuarded<std::set<std::tuple<AgentBounds, TilePosition>>> mProcessingTiles;
 | |
|         std::map<std::tuple<AgentBounds, TilePosition>, std::chrono::steady_clock::time_point> mLastUpdates;
 | |
|         std::set<std::tuple<AgentBounds, TilePosition>> mPresentTiles;
 | |
|         std::vector<std::thread> mThreads;
 | |
|         std::unique_ptr<DbWorker> mDbWorker;
 | |
|         std::atomic_size_t mDbGetTileHits{ 0 };
 | |
| 
 | |
|         void process() noexcept;
 | |
| 
 | |
|         JobStatus processJob(Job& job);
 | |
| 
 | |
|         inline JobStatus processInitialJob(Job& job, GuardedNavMeshCacheItem& navMeshCacheItem);
 | |
| 
 | |
|         inline JobStatus processJobWithDbResult(Job& job, GuardedNavMeshCacheItem& navMeshCacheItem);
 | |
| 
 | |
|         inline JobStatus handleUpdateNavMeshStatus(UpdateNavMeshStatus status, const Job& job,
 | |
|             const GuardedNavMeshCacheItem& navMeshCacheItem, const RecastMesh& recastMesh);
 | |
| 
 | |
|         inline JobIt getNextJob() noexcept;
 | |
| 
 | |
|         void postThreadJob(JobIt job, std::deque<JobIt>& queue);
 | |
| 
 | |
|         bool lockTile(std::size_t jobId, const AgentBounds& agentBounds, const TilePosition& changedTile);
 | |
| 
 | |
|         void unlockTile(std::size_t jobId, const AgentBounds& agentBounds, const TilePosition& changedTile);
 | |
| 
 | |
|         inline std::size_t getTotalJobs() const;
 | |
| 
 | |
|         inline void cleanupLastUpdates() noexcept;
 | |
| 
 | |
|         inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener* listener);
 | |
| 
 | |
|         inline void waitUntilAllJobsDone();
 | |
|     };
 | |
| }
 | |
| 
 | |
| #endif
 |