diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 23525dea8d..83e9805f6e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -619,7 +619,7 @@ namespace MWWorld if (changeEvent) mCellChanged = true; - mNavigator.wait(*loadingListener); + mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); } void Scene::testExteriorCells() @@ -848,7 +848,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); - mNavigator.wait(*loadingListener); + mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index cb8ae41523..41671993da 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -127,7 +127,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -177,7 +177,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -209,7 +209,7 @@ namespace mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mPath.clear(); mOut = std::back_inserter(mPath); @@ -262,7 +262,7 @@ namespace mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -296,7 +296,7 @@ namespace mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mPath.clear(); mOut = std::back_inserter(mPath); @@ -355,7 +355,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -411,7 +411,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -459,7 +459,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mStart.z() = 300; @@ -507,7 +507,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -554,7 +554,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits::max(), -25, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -601,7 +601,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -645,15 +645,15 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->removeObject(ObjectId(&shape)); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -699,7 +699,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); Misc::Rng::init(42); @@ -748,7 +748,7 @@ namespace } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -791,7 +791,7 @@ namespace mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto start = std::chrono::steady_clock::now(); for (std::size_t i = 0; i < shapes.size(); ++i) @@ -800,7 +800,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); for (std::size_t i = 0; i < shapes.size(); ++i) { @@ -808,7 +808,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto duration = std::chrono::steady_clock::now() - start; @@ -831,7 +831,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk); @@ -862,7 +862,7 @@ namespace mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto navMeshes = mNavigator->getNavMeshes(); ASSERT_EQ(navMeshes.size(), 1); @@ -878,7 +878,7 @@ namespace oscillatingBoxShapePosition); mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); mNavigator->update(mPlayerPosition); - mNavigator->wait(mListener); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); } ASSERT_EQ(navMeshes.size(), 1); diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 3f7114b85c..bee4510b42 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -10,7 +10,9 @@ #include +#include #include +#include namespace { @@ -23,12 +25,14 @@ namespace } int getMinDistanceTo(const TilePosition& position, int maxDistance, - const std::map>& tilesPerHalfExtents) + const std::map>& tilesPerHalfExtents, + const std::set>& presentTiles) { int result = maxDistance; for (const auto& [halfExtents, tiles] : tilesPerHalfExtents) for (const TilePosition& tile : tiles) - result = std::min(result, getManhattanDistance(position, tile)); + if (presentTiles.find(std::make_tuple(halfExtents, tile)) == presentTiles.end()) + result = std::min(result, getManhattanDistance(position, tile)); return result; } } @@ -142,7 +146,7 @@ namespace DetourNavigator mHasJob.notify_all(); } - void AsyncNavMeshUpdater::wait(Loading::Listener& listener) + void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0) return; @@ -150,15 +154,26 @@ namespace DetourNavigator const std::size_t initialJobsLeft = getTotalJobs(); std::size_t maxProgress = initialJobsLeft + mThreads.size(); listener.setProgressRange(maxProgress); - const int minDistanceToPlayer = waitUntilJobsDone(initialJobsLeft, maxProgress, listener); - if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer) + switch (waitConditionType) { - mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); - listener.setProgress(maxProgress); + case WaitConditionType::requiredTilesPresent: + { + const int minDistanceToPlayer = waitUntilJobsDoneForNotPresentTiles(initialJobsLeft, maxProgress, listener); + if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer) + { + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + listener.setProgress(maxProgress); + } + break; + } + case WaitConditionType::allJobsDone: + waitUntilAllJobsDone(); + listener.setProgress(maxProgress); + break; } } - int AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) + int AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) { std::size_t prevJobsLeft = initialJobsLeft; std::size_t jobsDone = 0; @@ -174,9 +189,9 @@ namespace DetourNavigator minDistanceToPlayer = 0; return true; } - minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed); + minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles); for (const auto& [threadId, queue] : mThreadsQueues) - minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed); + minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed, mPresentTiles); return minDistanceToPlayer >= maxDistanceToPlayer; }; std::unique_lock lock(mMutex); @@ -199,6 +214,15 @@ namespace DetourNavigator return minDistanceToPlayer; } + void AsyncNavMeshUpdater::waitUntilAllJobsDone() + { + { + std::unique_lock lock(mMutex); + mDone.wait(lock, [this] { return mJobs.size() + getTotalThreadJobsUnsafe() == 0; }); + } + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + } + void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const { std::size_t jobs = 0; @@ -273,6 +297,11 @@ namespace DetourNavigator navMeshVersion); } + if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost) + mPresentTiles.erase(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); + else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored) + mPresentTiles.insert(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); + const auto finish = std::chrono::steady_clock::now(); writeDebugFiles(job, recastMesh.get()); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index fb95325191..e8b2611e97 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -6,6 +6,7 @@ #include "tilecachedrecastmeshmanager.hpp" #include "tileposition.hpp" #include "navmeshtilescache.hpp" +#include "waitconditiontype.hpp" #include @@ -61,7 +62,7 @@ namespace DetourNavigator void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles); - void wait(Loading::Listener& listener); + void wait(Loading::Listener& listener, WaitConditionType waitConditionType); void reportStats(unsigned int frameNumber, osg::Stats& stats) const; @@ -114,6 +115,7 @@ namespace DetourNavigator NavMeshTilesCache mNavMeshTilesCache; Misc::ScopeGuarded>> mProcessingTiles; std::map> mLastUpdates; + std::set> mPresentTiles; std::map mThreadsQueues; std::vector mThreads; @@ -143,7 +145,9 @@ namespace DetourNavigator void cleanupLastUpdates(); - int waitUntilJobsDone(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(); }; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 3ec5e5acc4..8cf4cb80e8 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -7,6 +7,7 @@ #include "objectid.hpp" #include "navmeshcacheitem.hpp" #include "recastmeshtiles.hpp" +#include "waitconditiontype.hpp" namespace ESM { @@ -165,9 +166,10 @@ namespace DetourNavigator virtual void setUpdatesEnabled(bool enabled) = 0; /** - * @brief wait locks thread until all tiles are updated from last update call. + * @brief wait locks thread until tiles are updated from last update call based on passed condition type. + * @param waitConditionType defines when waiting will stop */ - virtual void wait(Loading::Listener& listener) = 0; + virtual void wait(Loading::Listener& listener, WaitConditionType waitConditionType) = 0; /** * @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through. diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index d1e75b864a..1d02ba2660 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -153,9 +153,9 @@ namespace DetourNavigator mUpdatesEnabled = enabled; } - void NavigatorImpl::wait(Loading::Listener& listener) + void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { - mNavMeshManager.wait(listener); + mNavMeshManager.wait(listener, waitConditionType); } SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index a53c9608de..3249462616 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -48,7 +48,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool enabled) override; - void wait(Loading::Listener& listener) override; + void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index c21db2bf8d..2c12c45eb5 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -73,7 +73,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool /*enabled*/) override {} - void wait(Loading::Listener& /*listener*/) override {} + void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 71a4def4f7..8f1aa86d44 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -5,6 +5,7 @@ #include "makenavmesh.hpp" #include "navmeshcacheitem.hpp" #include "settings.hpp" +#include "waitconditiontype.hpp" #include @@ -190,9 +191,9 @@ namespace DetourNavigator " recastMeshManagerRevision=" << lastRevision; } - void NavMeshManager::wait(Loading::Listener& listener) + void NavMeshManager::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { - mAsyncNavMeshUpdater.wait(listener); + mAsyncNavMeshUpdater.wait(listener, waitConditionType); } SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index ce90aafc5d..a38e221a70 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -6,6 +6,7 @@ #include "offmeshconnectionsmanager.hpp" #include "sharednavmesh.hpp" #include "recastmeshtiles.hpp" +#include "waitconditiontype.hpp" #include @@ -45,7 +46,7 @@ namespace DetourNavigator void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); - void wait(Loading::Listener& listener); + void wait(Loading::Listener& listener, WaitConditionType waitConditionType); SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const; diff --git a/components/detournavigator/waitconditiontype.hpp b/components/detournavigator/waitconditiontype.hpp new file mode 100644 index 0000000000..06a5901287 --- /dev/null +++ b/components/detournavigator/waitconditiontype.hpp @@ -0,0 +1,13 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H + +namespace DetourNavigator +{ + enum class WaitConditionType + { + requiredTilesPresent, + allJobsDone, + }; +} + +#endif