From 605cb8db7c2f6fa8f9fea4e5ac5641867ba7a20e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 5 Sep 2021 17:43:46 +0200 Subject: [PATCH] Make sync terrain preloading sleep free This reduces average time spent on in. 5 milliseconds as a base precision is quite a lot considering that for 60 FPS frame time is 1000/16 = ~16.67 ms when it's a cell loading frame and there is more important work to do rather than sleeping. --- apps/openmw/mwworld/cellpreloader.cpp | 22 ++++++------- apps/openmw/mwworld/cellpreloader.hpp | 7 ++++- apps/openmw/mwworld/scene.cpp | 21 +++---------- components/CMakeLists.txt | 4 +++ components/loadinglistener/reporter.cpp | 41 +++++++++++++++++++++++++ components/loadinglistener/reporter.hpp | 32 +++++++++++++++++++ components/terrain/quadtreeworld.cpp | 13 +++++--- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/world.hpp | 7 ++++- 9 files changed, 113 insertions(+), 36 deletions(-) create mode 100644 components/loadinglistener/reporter.cpp create mode 100644 components/loadinglistener/reporter.hpp diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index a2167c562f..7b6f640373 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "../mwrender/landmanager.hpp" @@ -157,8 +158,6 @@ namespace MWWorld public: TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) - , mProgress(views.size()) - , mProgressRange(0) , mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) @@ -178,8 +177,9 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mLoadingReporter); } + mLoadingReporter.complete(); } void abort() override @@ -187,16 +187,17 @@ namespace MWWorld mAbort = true; } - int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; } - int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; } + void wait(Loading::Listener& listener) const + { + mLoadingReporter.wait(listener); + } private: std::atomic mAbort; - std::vector> mProgress; - int mProgressRange; std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions; + Loading::Reporter mLoadingReporter; }; /// Worker thread item: update the resource system's cache, effectively deleting unused entries. @@ -415,7 +416,7 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - bool CellPreloader::syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp) + bool CellPreloader::syncTerrainLoad(const std::vector &positions, double timestamp, Loading::Listener& listener) { if (!mTerrainPreloadItem) return true; @@ -435,9 +436,8 @@ namespace MWWorld } else { - progress = mTerrainPreloadItem->getProgress(); - progressRange = mTerrainPreloadItem->getProgressRange(); - return false; + mTerrainPreloadItem->wait(listener); + return true; } } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index e719f2e606..e2eea33146 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -29,6 +29,11 @@ namespace MWRender class LandManager; } +namespace Loading +{ + class Listener; +} + namespace MWWorld { class CellStore; @@ -72,7 +77,7 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); - bool syncTerrainLoad(const std::vector &positions, int& progress, int& progressRange, double timestamp); + bool syncTerrainLoad(const std::vector &positions, double timestamp, Loading::Listener& listener); void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos); private: diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 90e5bc265e..399d0e6d7f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1250,23 +1250,10 @@ namespace MWWorld Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); - int progress = 0, initialProgress = -1, progressRange = 0; - while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime())) - { - if (initialProgress == -1) - { - loadingListener->setLabel("#{sLoadingMessage4}"); - initialProgress = progress; - } - if (progress) - { - loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); - loadingListener->setProgress(progress-initialProgress); - } - else - loadingListener->setProgress(0); - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - } + + loadingListener->setLabel("#{sLoadingMessage4}"); + + while (!mPreloader->syncTerrainLoad(vec, mRendering.getReferenceTime(), *loadingListener)) {} } void Scene::reloadTerrain() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6e7d6e379d..7983de6190 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -197,6 +197,10 @@ add_component_dir(detournavigator navmeshcacheitem ) +add_component_dir(loadinglistener + reporter + ) + set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) diff --git a/components/loadinglistener/reporter.cpp b/components/loadinglistener/reporter.cpp new file mode 100644 index 0000000000..0ad04fded1 --- /dev/null +++ b/components/loadinglistener/reporter.cpp @@ -0,0 +1,41 @@ +#include "reporter.hpp" +#include "loadinglistener.hpp" + +#include +#include +#include + +namespace Loading +{ + void Reporter::addTotal(std::size_t value) + { + const std::lock_guard lock(mMutex); + mTotal += value; + mUpdated.notify_all(); + } + + void Reporter::addProgress(std::size_t value) + { + const std::lock_guard lock(mMutex); + mProgress += value; + mUpdated.notify_all(); + } + + void Reporter::complete() + { + const std::lock_guard lock(mMutex); + mDone = true; + mUpdated.notify_all(); + } + + void Reporter::wait(Listener& listener) const + { + std::unique_lock lock(mMutex); + while (!mDone) + { + listener.setProgressRange(mTotal); + listener.setProgress(mProgress); + mUpdated.wait(lock); + } + } +} diff --git a/components/loadinglistener/reporter.hpp b/components/loadinglistener/reporter.hpp new file mode 100644 index 0000000000..b59c519082 --- /dev/null +++ b/components/loadinglistener/reporter.hpp @@ -0,0 +1,32 @@ +#ifndef COMPONENTS_LOADINGLISTENER_REPORTER_H +#define COMPONENTS_LOADINGLISTENER_REPORTER_H + +#include +#include +#include + +namespace Loading +{ + class Listener; + + class Reporter + { + public: + void addTotal(std::size_t value); + + void addProgress(std::size_t value); + + void complete(); + + void wait(Listener& listener) const; + + private: + std::size_t mProgress = 0; + std::size_t mTotal = 0; + bool mDone = false; + mutable std::mutex mMutex; + mutable std::condition_variable mUpdated; + }; +} + +#endif diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index e26fc1b617..9ca504ebeb 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "quadtreenode.hpp" #include "storage.hpp" @@ -496,7 +497,7 @@ View* QuadTreeWorld::createView() return mViewDataMap->createIndependentView(); } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, Loading::Reporter& reporter) { ensureQuadTreeBuilt(); @@ -506,16 +507,18 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, grid); mRootNode->traverseNodes(vd, viewPoint, &lodCallback); - if (!progressTotal) - for (unsigned int i=0; igetNumEntries(); ++i) - progressTotal += vd->getEntry(i).mNode->getSize(); + std::size_t progressTotal = 0; + for (unsigned int i = 0, n = vd->getNumEntries(); i < n; ++i) + progressTotal += vd->getEntry(i).mNode->getSize(); + + reporter.addTotal(progressTotal); const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); - progress += entry.mNode->getSize(); + reporter.addProgress(entry.mNode->getSize()); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 2ddea42049..3a2aa8349d 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -39,7 +39,7 @@ namespace Terrain void unloadCell(int x, int y) override; View* createView() override; - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) override; + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, Loading::Reporter& reporter) override; bool storeView(const View* view, double referenceTime) override; void rebuildViews() override; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 5797d894ef..b62a1cb568 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -32,6 +32,11 @@ namespace SceneUtil class WorkQueue; } +namespace Loading +{ + class Reporter; +} + namespace Terrain { class Storage; @@ -148,7 +153,7 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort, Loading::Reporter& reporter) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe.