From f169f8e6f0325bfb1f9e2c1cb337cde53abd0ce6 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 5 May 2021 18:13:17 +0200 Subject: [PATCH] Wait until navmesh is generated for interior cells Add special loading progress bar. It should be fast enough to not keep loading screen for noticably long but will provide better pathfinding for actors inside interior cells. --- apps/openmw/mwworld/scene.cpp | 2 + .../detournavigator/navigator.cpp | 46 ++++++++++--------- .../detournavigator/asyncnavmeshupdater.cpp | 46 +++++++++++++++++-- .../detournavigator/asyncnavmeshupdater.hpp | 11 ++++- components/detournavigator/navigator.hpp | 7 ++- components/detournavigator/navigatorimpl.cpp | 4 +- components/detournavigator/navigatorimpl.hpp | 2 +- components/detournavigator/navigatorstub.hpp | 7 ++- components/detournavigator/navmeshmanager.cpp | 4 +- components/detournavigator/navmeshmanager.hpp | 2 +- components/misc/guarded.hpp | 4 +- 11 files changed, 98 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dae703c8c..0cfca3be2 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -847,6 +847,8 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + + mNavigator.wait(*loadingListener); } 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 619276588..8d92b48e0 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ namespace std::back_insert_iterator> mOut; float mStepSize; AreaCosts mAreaCosts; + Loading::Listener mListener; DetourNavigatorNavigatorTest() : mPlayerPosition(0, 0, 0) @@ -124,7 +126,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -174,7 +176,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -206,7 +208,7 @@ namespace mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mPath.clear(); mOut = std::back_inserter(mPath); @@ -259,7 +261,7 @@ namespace mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -293,7 +295,7 @@ namespace mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mPath.clear(); mOut = std::back_inserter(mPath); @@ -352,7 +354,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -408,7 +410,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -456,7 +458,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(); + mNavigator->wait(mListener); mStart.x() = 0; mStart.z() = 300; @@ -504,7 +506,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(); + mNavigator->wait(mListener); mStart.x() = 0; mEnd.x() = 0; @@ -551,7 +553,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(); + mNavigator->wait(mListener); mStart.x() = 0; mEnd.x() = 0; @@ -598,7 +600,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(); + mNavigator->wait(mListener); mStart.x() = 0; mEnd.x() = 0; @@ -642,15 +644,15 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mNavigator->removeObject(ObjectId(&shape)); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -696,7 +698,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); Misc::Rng::init(42); @@ -745,7 +747,7 @@ namespace } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -788,7 +790,7 @@ namespace mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto start = std::chrono::steady_clock::now(); for (std::size_t i = 0; i < shapes.size(); ++i) @@ -797,7 +799,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); for (std::size_t i = 0; i < shapes.size(); ++i) { @@ -805,7 +807,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto duration = std::chrono::steady_clock::now() - start; @@ -828,7 +830,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk); @@ -859,7 +861,7 @@ namespace mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); const auto navMeshes = mNavigator->getNavMeshes(); ASSERT_EQ(navMeshes.size(), 1); @@ -875,7 +877,7 @@ namespace oscillatingBoxShapePosition); mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener); } ASSERT_EQ(navMeshes.size(), 1); diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 7248850a6..124d3c4e0 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -111,13 +112,44 @@ namespace DetourNavigator mHasJob.notify_all(); } - void AsyncNavMeshUpdater::wait() + void AsyncNavMeshUpdater::wait(Loading::Listener& listener) { + listener.setLabel("Building navigation mesh"); + const std::size_t initialJobsLeft = getTotalJobs(); + std::size_t maxProgress = initialJobsLeft + mThreads.size(); + listener.setProgressRange(maxProgress); + waitUntilJobsDone(initialJobsLeft, maxProgress, listener); + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + listener.setProgress(maxProgress); + } + + void AsyncNavMeshUpdater::waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) + { + std::size_t prevJobsLeft = initialJobsLeft; + std::size_t jobsDone = 0; + std::size_t jobsLeft = 0; + const auto isDone = [&] { - std::unique_lock lock(mMutex); - mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; }); + jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe(); + return jobsLeft == 0; + }; + std::unique_lock lock(mMutex); + while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone)) + { + if (maxProgress < jobsLeft) + { + maxProgress = jobsLeft + mThreads.size(); + listener.setProgressRange(maxProgress); + listener.setProgress(jobsDone); + } + else if (jobsLeft < prevJobsLeft) + { + const std::size_t newJobsDone = prevJobsLeft - jobsLeft; + jobsDone += newJobsDone; + prevJobsLeft = jobsLeft; + listener.increaseProgress(newJobsDone); + } } - mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); } void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const @@ -381,6 +413,12 @@ namespace DetourNavigator mProcessed.notify_all(); } + std::size_t AsyncNavMeshUpdater::getTotalJobs() const + { + const std::scoped_lock lock(mMutex); + return mJobs.size() + getTotalThreadJobsUnsafe(); + } + std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const { return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0), diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 53e7fd7c1..fcf57d683 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -20,6 +20,11 @@ class dtNavMesh; +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { enum class ChangeType @@ -55,7 +60,7 @@ namespace DetourNavigator void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles); - void wait(); + void wait(Loading::Listener& listener); void reportStats(unsigned int frameNumber, osg::Stats& stats) const; @@ -131,9 +136,13 @@ namespace DetourNavigator void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); + inline std::size_t getTotalJobs() const; + inline std::size_t getTotalThreadJobsUnsafe() const; void cleanupLastUpdates(); + + void waitUntilJobsDone(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); }; } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index edf597348..3ec5e5acc 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -14,6 +14,11 @@ namespace ESM struct Pathgrid; } +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { struct ObjectShapes @@ -162,7 +167,7 @@ namespace DetourNavigator /** * @brief wait locks thread until all tiles are updated from last update call. */ - virtual void wait() = 0; + virtual void wait(Loading::Listener& listener) = 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 abfb20ba8..d1e75b864 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -153,9 +153,9 @@ namespace DetourNavigator mUpdatesEnabled = enabled; } - void NavigatorImpl::wait() + void NavigatorImpl::wait(Loading::Listener& listener) { - mNavMeshManager.wait(); + mNavMeshManager.wait(listener); } SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 74fff0dea..a53c9608d 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -48,7 +48,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool enabled) override; - void wait() override; + void wait(Loading::Listener& listener) override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index f6892bf1b..c21db2bf8 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -3,6 +3,11 @@ #include "navigator.hpp" +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { class NavigatorStub final : public Navigator @@ -68,7 +73,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool /*enabled*/) override {} - void wait() override {} + void wait(Loading::Listener& /*listener*/) override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 43d330648..21022d60b 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -188,9 +188,9 @@ namespace DetourNavigator " recastMeshManagerRevision=" << lastRevision; } - void NavMeshManager::wait() + void NavMeshManager::wait(Loading::Listener& listener) { - mAsyncNavMeshUpdater.wait(); + mAsyncNavMeshUpdater.wait(listener); } SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index f3861f8f2..ce90aafc5 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -45,7 +45,7 @@ namespace DetourNavigator void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); - void wait(); + void wait(Loading::Listener& listener); SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const; diff --git a/components/misc/guarded.hpp b/components/misc/guarded.hpp index 55a2c670c..7f1005fc3 100644 --- a/components/misc/guarded.hpp +++ b/components/misc/guarded.hpp @@ -75,7 +75,7 @@ namespace Misc return Locked(mMutex, mValue); } - Locked lockConst() + Locked lockConst() const { return Locked(mMutex, mValue); } @@ -88,7 +88,7 @@ namespace Misc } private: - std::mutex mMutex; + mutable std::mutex mMutex; T mValue; }; }