diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c3ace78ac8..9e6643a577 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include @@ -419,7 +420,14 @@ bool OMW::Engine::frame(float frametime) mWindowManager->update(frametime); } - if (stats->collectStats("resource")) + const bool reportResource = stats->collectStats("resource"); + + if (reportResource) + stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getSize()); + + mUnrefQueue->flush(*mWorkQueue); + + if (reportResource) { stats->setAttribute(frameNumber, "FrameNumber", frameNumber); @@ -492,6 +500,7 @@ OMW::Engine::~Engine() mScriptContext = nullptr; + mUnrefQueue = nullptr; mWorkQueue = nullptr; mViewer = nullptr; @@ -751,6 +760,7 @@ void OMW::Engine::prepareEngine() if (numThreads <= 0) throw std::runtime_error("Invalid setting: 'preload num threads' must be >0"); mWorkQueue = new SceneUtil::WorkQueue(numThreads); + mUnrefQueue = std::make_unique(); mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation( mWorkQueue, @@ -842,7 +852,7 @@ void OMW::Engine::prepareEngine() } // Create the world - mWorld = std::make_unique(mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(), + mWorld = std::make_unique(mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(), *mUnrefQueue, mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder.get(), mActivationDistanceOverride, mCellName, mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()); mWorld->setupPlayer(); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 329993a93e..cc6dd732e5 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -22,6 +22,7 @@ namespace SceneUtil { class WorkQueue; class AsyncScreenCaptureOperation; + class UnrefQueue; } namespace VFS @@ -120,6 +121,7 @@ namespace OMW std::unique_ptr mVFS; std::unique_ptr mResourceSystem; osg::ref_ptr mWorkQueue; + std::unique_ptr mUnrefQueue; std::unique_ptr mWorld; std::unique_ptr mSoundManager; std::unique_ptr mScriptManager; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index c241892987..88b86d8e8a 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" @@ -17,9 +18,11 @@ namespace MWRender { -Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode) +Objects::Objects(Resource::ResourceSystem* resourceSystem, const osg::ref_ptr& rootNode, + SceneUtil::UnrefQueue& unrefQueue) : mRootNode(rootNode) , mResourceSystem(resourceSystem) + , mUnrefQueue(unrefQueue) { } @@ -116,6 +119,7 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr) if(iter != mObjects.end()) { iter->second->removeFromScene(); + mUnrefQueue.push(std::move(iter->second)); mObjects.erase(iter); if (ptr.getClass().isActor()) @@ -150,6 +154,7 @@ void Objects::removeCell(const MWWorld::CellStore* store) } iter->second->removeFromScene(); + mUnrefQueue.push(std::move(iter->second)); iter = mObjects.erase(iter); } else diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 793239c26e..2ca3da0c86 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -24,6 +24,11 @@ namespace MWWorld class CellStore; } +namespace SceneUtil +{ + class UnrefQueue; +} + namespace MWRender{ class Animation; @@ -57,15 +62,15 @@ class Objects typedef std::map > CellMap; CellMap mCellSceneNodes; PtrAnimationMap mObjects; - osg::ref_ptr mRootNode; - Resource::ResourceSystem* mResourceSystem; + SceneUtil::UnrefQueue& mUnrefQueue; void insertBegin(const MWWorld::Ptr& ptr); public: - Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode); + Objects(Resource::ResourceSystem* resourceSystem, const osg::ref_ptr& rootNode, + SceneUtil::UnrefQueue& unrefQueue); ~Objects(); /// @param animated Attempt to load separate keyframes from a .kf file matching the model file? diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7245b62e6b..6f7ec25b98 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -367,8 +367,9 @@ namespace MWRender }; RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, - Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore) + Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& resourcePath, + DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore, + SceneUtil::UnrefQueue& unrefQueue) : mSkyBlending(Settings::Manager::getBool("sky blending", "Fog")) , mViewer(viewer) , mRootNode(rootNode) @@ -479,7 +480,7 @@ namespace MWRender mRecastMesh = std::make_unique(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")); mPathgrid = std::make_unique(mRootNode); - mObjects = std::make_unique(mResourceSystem, sceneRoot); + mObjects = std::make_unique(mResourceSystem, sceneRoot, unrefQueue); if (getenv("OPENMW_DONT_PRECOMPILE") == nullptr) { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 0424d20b23..10b4872650 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -60,6 +60,7 @@ namespace SceneUtil class ShadowManager; class WorkQueue; class LightManager; + class UnrefQueue; } namespace DetourNavigator @@ -100,9 +101,10 @@ namespace MWRender class RenderingManager : public MWRender::RenderingInterface { public: - RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, - Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore); + RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, + Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& resourcePath, + DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore, + SceneUtil::UnrefQueue& unrefQueue); ~RenderingManager(); osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f24151fee0..517a5bd610 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -139,6 +140,7 @@ namespace MWWorld osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, + SceneUtil::UnrefQueue& unrefQueue, const Files::Collections& fileCollections, const std::vector& contentFiles, const std::vector& groundcoverFiles, @@ -187,7 +189,8 @@ namespace MWWorld mNavigator = DetourNavigator::makeNavigatorStub(); } - mRendering = std::make_unique(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator, mGroundcoverStore); + mRendering = std::make_unique(viewer, rootNode, resourceSystem, workQueue, + resourcePath, *mNavigator, mGroundcoverStore, unrefQueue); mProjectileManager = std::make_unique(mRendering->getLightRoot()->asGroup(), resourceSystem, mRendering.get(), mPhysics.get()); mRendering->preloadCommonAssets(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1849937c63..9310069dc1 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -38,6 +38,7 @@ namespace Resource namespace SceneUtil { class WorkQueue; + class UnrefQueue; } namespace ESM @@ -192,6 +193,7 @@ namespace MWWorld osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, + SceneUtil::UnrefQueue& unrefQueue, const Files::Collections& fileCollections, const std::vector& contentFiles, const std::vector& groundcoverFiles, diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8dcb1ce64f..93901b6f83 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -61,7 +61,7 @@ add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue pathgridutil waterutil writescene serialize optimizer actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt - screencapture depth color riggeometryosgaextension extradata + screencapture depth color riggeometryosgaextension extradata unrefqueue ) add_component_dir (nif diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index a204bf61f8..c5229f491a 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -417,6 +417,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) "Compiling", "WorkQueue", "WorkThread", + "UnrefQueue", "", "Texture", "StateSet", diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp new file mode 100644 index 0000000000..57697e153c --- /dev/null +++ b/components/sceneutil/unrefqueue.cpp @@ -0,0 +1,28 @@ +#include "unrefqueue.hpp" + +namespace SceneUtil +{ + namespace + { + struct ClearVector final : SceneUtil::WorkItem + { + std::vector> mObjects; + + explicit ClearVector(std::vector>&& objects) + : mObjects(std::move(objects)) {} + + void doWork() override { mObjects.clear(); } + }; + } + + void UnrefQueue::flush(SceneUtil::WorkQueue& workQueue) + { + if (mObjects.empty()) + return; + + // Move only objects to keep allocated storage in mObjects + workQueue.addWorkItem(new ClearVector(std::vector>( + std::move_iterator(mObjects.begin()), std::move_iterator(mObjects.end())))); + mObjects.clear(); + } +} diff --git a/components/sceneutil/unrefqueue.hpp b/components/sceneutil/unrefqueue.hpp new file mode 100644 index 0000000000..b2fdbc480a --- /dev/null +++ b/components/sceneutil/unrefqueue.hpp @@ -0,0 +1,36 @@ +#ifndef OPENMW_COMPONENTS_UNREFQUEUE_H +#define OPENMW_COMPONENTS_UNREFQUEUE_H + +#include "workqueue.hpp" + +#include +#include + +#include + +namespace SceneUtil +{ + class WorkQueue; + + /// @brief Handles unreferencing of objects through the WorkQueue. Typical use scenario + /// would be the main thread pushing objects that are no longer needed, and the background thread deleting them. + class UnrefQueue + { + public: + /// Adds an object to the list of objects to be unreferenced. Call from the main thread. + void push(osg::ref_ptr&& obj) { mObjects.push_back(std::move(obj)); } + + void push(const osg::ref_ptr& obj) { mObjects.push_back(obj); } + + /// Adds a WorkItem to the given WorkQueue that will clear the list of objects in a worker thread, + /// thus unreferencing them. Call from the main thread. + void flush(SceneUtil::WorkQueue& workQueue); + + std::size_t getSize() const { return mObjects.size(); } + + private: + std::vector> mObjects; + }; +} + +#endif