diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index f58ebb917..516d10b95 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" @@ -85,9 +86,10 @@ namespace namespace MWRender { -Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode) +Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode, SceneUtil::UnrefQueue* unrefQueue) : mRootNode(rootNode) , mResourceSystem(resourceSystem) + , mUnrefQueue(unrefQueue) { } @@ -186,7 +188,11 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr) delete iter->second; mObjects.erase(iter); + if (mUnrefQueue.get()) + mUnrefQueue->push(ptr.getRefData().getBaseNode()); + ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); + ptr.getRefData().setBaseNode(NULL); return true; } @@ -200,6 +206,8 @@ void Objects::removeCell(const MWWorld::CellStore* store) { if(iter->first.getCell() == store) { + if (mUnrefQueue.get()) + mUnrefQueue->push(iter->second->getObjectRoot()); delete iter->second; mObjects.erase(iter++); } @@ -211,6 +219,8 @@ void Objects::removeCell(const MWWorld::CellStore* store) if(cell != mCellSceneNodes.end()) { cell->second->getParent(0)->removeChild(cell->second); + if (mUnrefQueue.get()) + mUnrefQueue->push(cell->second); mCellSceneNodes.erase(cell); } } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 3d0c92cb4..5b91abea4 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -30,6 +30,11 @@ namespace MWWorld class CellStore; } +namespace SceneUtil +{ + class UnrefQueue; +} + namespace MWRender{ class Animation; @@ -65,12 +70,14 @@ class Objects{ osg::ref_ptr mRootNode; - void insertBegin(const MWWorld::Ptr& ptr); - Resource::ResourceSystem* mResourceSystem; + osg::ref_ptr mUnrefQueue; + + void insertBegin(const MWWorld::Ptr& ptr); + public: - Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode); + Objects(Resource::ResourceSystem* resourceSystem, 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 8c23a0707..5a9cd59fc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -156,6 +157,7 @@ namespace MWRender , mRootNode(rootNode) , mResourceSystem(resourceSystem) , mWorkQueue(new SceneUtil::WorkQueue) + , mUnrefQueue(new SceneUtil::UnrefQueue) , mFogDepth(0.f) , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) @@ -176,7 +178,7 @@ namespace MWRender mPathgrid.reset(new Pathgrid(mRootNode)); - mObjects.reset(new Objects(mResourceSystem, lightRoot)); + mObjects.reset(new Objects(mResourceSystem, lightRoot, mUnrefQueue.get())); mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); @@ -187,7 +189,7 @@ namespace MWRender mWater.reset(new Water(mRootNode, lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); mTerrain.reset(new Terrain::TerrainGrid(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain)); + new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain, mUnrefQueue.get())); mCamera.reset(new Camera(mViewer->getCamera())); @@ -437,6 +439,8 @@ namespace MWRender void RenderingManager::update(float dt, bool paused) { + mUnrefQueue->flush(mWorkQueue.get()); + if (!paused) { mEffectManager->update(dt); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 72f322d2c..3d6a4ac3a 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -45,6 +45,7 @@ namespace Fallback namespace SceneUtil { class WorkQueue; + class UnrefQueue; } namespace MWRender @@ -200,6 +201,7 @@ namespace MWRender Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mWorkQueue; + osg::ref_ptr mUnrefQueue; osg::ref_ptr mSunLight; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 26e8f7b6a..81e6defe4 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -46,7 +46,7 @@ add_component_dir (resource add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller - lightmanager lightutil positionattitudetransform workqueue + lightmanager lightutil positionattitudetransform workqueue unrefqueue ) add_component_dir (nif diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp new file mode 100644 index 000000000..23b1359e5 --- /dev/null +++ b/components/sceneutil/unrefqueue.cpp @@ -0,0 +1,46 @@ +#include "unrefqueue.hpp" + +#include +//#include +//#include + +#include + +namespace SceneUtil +{ + + class UnrefWorkItem : public SceneUtil::WorkItem + { + public: + std::vector > mObjects; + + virtual void doWork() + { + //osg::Timer timer; + //size_t objcount = mObjects.size(); + mObjects.clear(); + //std::cout << "cleared " << objcount << " objects in " << timer.time_m() << std::endl; + } + }; + + UnrefQueue::UnrefQueue() + { + mWorkItem = new UnrefWorkItem; + } + + void UnrefQueue::push(osg::Object *obj) + { + mWorkItem->mObjects.push_back(obj); + } + + void UnrefQueue::flush(SceneUtil::WorkQueue *workQueue) + { + if (mWorkItem->mObjects.empty()) + return; + + workQueue->addWorkItem(mWorkItem); + + mWorkItem = new UnrefWorkItem; + } + +} diff --git a/components/sceneutil/unrefqueue.hpp b/components/sceneutil/unrefqueue.hpp new file mode 100644 index 000000000..d0bec061c --- /dev/null +++ b/components/sceneutil/unrefqueue.hpp @@ -0,0 +1,37 @@ +#ifndef OPENMW_COMPONENTS_UNREFQUEUE_H +#define OPENMW_COMPONENTS_UNREFQUEUE_H + +#include +#include + +namespace osg +{ + class Object; +} + +namespace SceneUtil +{ + class WorkQueue; + class UnrefWorkItem; + + /// @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 osg::Referenced + { + public: + UnrefQueue(); + + /// Adds an object to the list of objects to be unreferenced. Call from the main thread. + void push(osg::Object* 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); + + private: + osg::ref_ptr mWorkItem; + }; + +} + +#endif diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4f7a0772b..4b67371e0 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -46,11 +47,10 @@ namespace namespace Terrain { -TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, - Storage* storage, int nodeMask) +TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue) : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) , mNumSplits(4) - , mKdTreeBuilder(new osg::KdTreeBuilder) + , mUnrefQueue(unrefQueue) { mCache = BufferCache((storage->getCellVertices()-1)/static_cast(mNumSplits) + 1); } @@ -212,13 +212,6 @@ void TerrainGrid::loadCell(int x, int y) element->mNode = terrainNode; mTerrainRoot->addChild(element->mNode); - // kdtree probably not needed with mNumSplits=4 - /* - // build a kdtree to speed up intersection tests with the terrain - // Note, the build could be optimized using a custom kdtree builder, since we know that the terrain can be represented by a quadtree - geode->accept(*mKdTreeBuilder); - */ - mGrid[std::make_pair(x,y)] = element.release(); } @@ -230,6 +223,10 @@ void TerrainGrid::unloadCell(int x, int y) GridElement* element = it->second; mTerrainRoot->removeChild(element->mNode); + + if (mUnrefQueue.get()) + mUnrefQueue->push(element->mNode); + delete element; mGrid.erase(it); @@ -240,7 +237,11 @@ void TerrainGrid::clearCache() for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) { if (it->second->referenceCount() <= 1) + { + if (mUnrefQueue.get()) + mUnrefQueue->push(it->second); mTextureCache.erase(it++); + } else ++it; } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 81bfc4211..5708e7068 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -6,9 +6,9 @@ #include "world.hpp" #include "material.hpp" -namespace osg +namespace SceneUtil { - class KdTreeBuilder; + class UnrefQueue; } namespace Terrain @@ -20,8 +20,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, - Storage* storage, int nodeMask); + TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue = NULL); ~TerrainGrid(); virtual void loadCell(int x, int y); @@ -42,7 +41,7 @@ namespace Terrain typedef std::map, GridElement*> Grid; Grid mGrid; - osg::ref_ptr mKdTreeBuilder; + osg::ref_ptr mUnrefQueue; }; }