From e28dc3e72fea00ebde993cdb5cc3ea01e84d1b7c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 18:33:02 +0100 Subject: [PATCH] Preload instances in SceneManager --- apps/opencs/view/render/object.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 6 +- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/effectmanager.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 12 ++-- apps/openmw/mwrender/weaponanimation.cpp | 2 +- apps/openmw/mwworld/cellpreloader.cpp | 9 ++- apps/openmw/mwworld/cellpreloader.hpp | 2 + apps/openmw/mwworld/projectilemanager.cpp | 2 +- apps/openmw/mwworld/scene.cpp | 2 + components/CMakeLists.txt | 2 +- components/resource/multiobjectcache.cpp | 79 ++++++++++++++++++++++ components/resource/multiobjectcache.hpp | 47 +++++++++++++ components/resource/scenemanager.cpp | 39 +++++++++-- components/resource/scenemanager.hpp | 24 +++++-- 16 files changed, 207 insertions(+), 27 deletions(-) create mode 100644 components/resource/multiobjectcache.cpp create mode 100644 components/resource/multiobjectcache.hpp diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index b980b658a..33939625d 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -100,7 +100,7 @@ void CSVRender::Object::update() { std::string path = "meshes\\" + model; - mResourceSystem->getSceneManager()->createInstance(path, mBaseNode); + mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); } catch (std::exception& e) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c86878e3d..398fe5322 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -997,7 +997,7 @@ namespace MWRender if (!forceskeleton) { - osg::ref_ptr created = mResourceSystem->getSceneManager()->createInstance(model, mInsert); + osg::ref_ptr created = mResourceSystem->getSceneManager()->getInstance(model, mInsert); mObjectRoot = created->asGroup(); if (!mObjectRoot) { @@ -1009,7 +1009,7 @@ namespace MWRender } else { - osg::ref_ptr created = mResourceSystem->getSceneManager()->createInstance(model); + osg::ref_ptr created = mResourceSystem->getSceneManager()->getInstance(model); osg::ref_ptr skel = dynamic_cast(created.get()); if (!skel) { @@ -1136,7 +1136,7 @@ namespace MWRender parentNode = found->second; } - osg::ref_ptr node = mResourceSystem->getSceneManager()->createInstance(model, parentNode); + osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(model, parentNode); node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 7c447182f..9cd35ecde 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -105,7 +105,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) else bonename = "Shield Bone"; - osg::ref_ptr node = mResourceSystem->getSceneManager()->createInstance(item.getClass().getModel(item)); + osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(item.getClass().getModel(item)); osg::ref_ptr attached = SceneUtil::attach(node, mObjectRoot, bonename, bonename); mResourceSystem->getSceneManager()->notifyAttached(attached); diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index c4e457a1f..e2773f2dc 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -27,7 +27,7 @@ EffectManager::~EffectManager() void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale) { - osg::ref_ptr node = mResourceSystem->getSceneManager()->createInstance(model); + osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(model); node->setNodeMask(Mask_Effect); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 709653cd8..eee23aa31 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -647,7 +647,7 @@ void NpcAnimation::updateParts() PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor) { - osg::ref_ptr instance = mResourceSystem->getSceneManager()->createInstance(model); + osg::ref_ptr instance = mResourceSystem->getSceneManager()->getInstance(model); osg::ref_ptr attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, bonename); mResourceSystem->getSceneManager()->notifyAttached(attached); if (enchantedGlow) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index dafb2bb4b..40283ba27 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1146,7 +1146,7 @@ void SkyManager::create() { assert(!mCreated); - mAtmosphereDay = mSceneManager->createInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot); + mAtmosphereDay = mSceneManager->getInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot); ModVertexAlphaVisitor modAtmosphere(0); mAtmosphereDay->accept(modAtmosphere); @@ -1159,9 +1159,9 @@ void SkyManager::create() osg::ref_ptr atmosphereNight; if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) - atmosphereNight = mSceneManager->createInstance("meshes/sky_night_02.nif", mAtmosphereNightNode); + atmosphereNight = mSceneManager->getInstance("meshes/sky_night_02.nif", mAtmosphereNightNode); else - atmosphereNight = mSceneManager->createInstance("meshes/sky_night_01.nif", mAtmosphereNightNode); + atmosphereNight = mSceneManager->getInstance("meshes/sky_night_01.nif", mAtmosphereNightNode); atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); ModVertexAlphaVisitor modStars(2); atmosphereNight->accept(modStars); @@ -1176,14 +1176,14 @@ void SkyManager::create() mCloudNode = new osg::PositionAttitudeTransform; mEarlyRenderBinRoot->addChild(mCloudNode); - mCloudMesh = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); + mCloudMesh = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode); ModVertexAlphaVisitor modClouds(1); mCloudMesh->accept(modClouds); mCloudUpdater = new CloudUpdater; mCloudUpdater->setOpacity(1.f); mCloudMesh->addUpdateCallback(mCloudUpdater); - mCloudMesh2 = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); + mCloudMesh2 = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode); mCloudMesh2->accept(modClouds); mCloudUpdater2 = new CloudUpdater; mCloudUpdater2->setOpacity(0.f); @@ -1537,7 +1537,7 @@ void SkyManager::setWeather(const WeatherResult& weather) mParticleNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mParticleNode); } - mParticleEffect = mSceneManager->createInstance(mCurrentParticleEffect, mParticleNode); + mParticleEffect = mSceneManager->getInstance(mCurrentParticleEffect, mParticleNode); SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr(new SceneUtil::FrameTimeSource)); mParticleEffect->accept(assignVisitor); diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 4328d5a3d..8fd294ccd 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -84,7 +84,7 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor) return; std::string model = ammo->getClass().getModel(*ammo); - osg::ref_ptr arrow = getResourceSystem()->getSceneManager()->createInstance(model, parent); + osg::ref_ptr arrow = getResourceSystem()->getSceneManager()->getInstance(model, parent); mAmmunition = PartHolderPtr(new PartHolder(arrow)); } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 2591e8c89..c6fa3b949 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -86,7 +86,7 @@ namespace MWWorld //std::cout << "preloading " << mesh << std::endl; - mPreloadedNodes.push_back(mSceneManager->getTemplate(mesh)); + mPreloadedNodes.push_back(mSceneManager->cacheInstance(mesh)); mPreloadedShapes.push_back(mBulletShapeManager->getShape(mesh)); size_t slashpos = mesh.find_last_of("/\\"); @@ -104,8 +104,6 @@ namespace MWWorld } } - - // TODO: do a createInstance() and hold on to it since we can make use of it when the cell goes active } catch (std::exception& e) { @@ -185,6 +183,11 @@ namespace MWWorld mPreloadCells[cell] = PreloadEntry(timestamp, item); } + void CellPreloader::notifyLoaded(CellStore *cell) + { + mPreloadCells.erase(cell); + } + void CellPreloader::updateCache(double timestamp) { // TODO: add settings for a minimum/maximum size of the cache diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index c7495182b..55e40dcbd 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -24,6 +24,8 @@ namespace MWWorld /// @note The cell itself must be in State_Loaded or State_Preloaded. void preload(MWWorld::CellStore* cell, double timestamp); + void notifyLoaded(MWWorld::CellStore* cell); + /// Removes preloaded cells that have not had a preload request for a while. void updateCache(double timestamp); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index d1faf621c..b8150ce9c 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -92,7 +92,7 @@ namespace MWWorld attachTo = rotateNode; } - mResourceSystem->getSceneManager()->createInstance(model, attachTo); + mResourceSystem->getSceneManager()->getInstance(model, attachTo); SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; state.mNode->accept(disableFreezeOnCullVisitor); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4c843133d..46d75daaf 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -296,6 +296,8 @@ namespace MWWorld if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) mRendering.configureAmbient(cell->getCell()); } + + mPreloader->notifyLoaded(cell); } void Scene::changeToVoid() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 81e6defe4..0ac32f65e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -41,7 +41,7 @@ add_component_dir (vfs ) add_component_dir (resource - scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache resourcesystem resourcemanager + scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager ) add_component_dir (sceneutil diff --git a/components/resource/multiobjectcache.cpp b/components/resource/multiobjectcache.cpp new file mode 100644 index 000000000..352715f19 --- /dev/null +++ b/components/resource/multiobjectcache.cpp @@ -0,0 +1,79 @@ +#include "multiobjectcache.hpp" + +#include + +#include + +namespace Resource +{ + + MultiObjectCache::MultiObjectCache() + { + + } + + MultiObjectCache::~MultiObjectCache() + { + + } + + void MultiObjectCache::removeUnreferencedObjectsInCache() + { + std::vector > objectsToRemove; + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + + // Remove unreferenced entries from object cache + ObjectCacheMap::iterator oitr = _objectCache.begin(); + while(oitr != _objectCache.end()) + { + if (oitr->second->referenceCount() <= 1) + { + objectsToRemove.push_back(oitr->second); + _objectCache.erase(oitr++); + } + else + { + ++oitr; + } + } + } + + // note, actual unref happens outside of the lock + objectsToRemove.clear(); + } + + void MultiObjectCache::addEntryToObjectCache(const std::string &filename, osg::Object *object) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + _objectCache.insert(std::make_pair(filename, object)); + } + + osg::ref_ptr MultiObjectCache::takeFromObjectCache(const std::string &fileName) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheMap::iterator found = _objectCache.find(fileName); + if (found == _objectCache.end()) + return osg::ref_ptr(); + else + { + osg::ref_ptr object = found->second; + _objectCache.erase(found); + return object; + } + } + + void MultiObjectCache::releaseGLObjects(osg::State *state) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + + for(ObjectCacheMap::iterator itr = _objectCache.begin(); + itr != _objectCache.end(); + ++itr) + { + osg::Object* object = itr->second.get(); + object->releaseGLObjects(state); + } + } + +} diff --git a/components/resource/multiobjectcache.hpp b/components/resource/multiobjectcache.hpp new file mode 100644 index 000000000..b677100f0 --- /dev/null +++ b/components/resource/multiobjectcache.hpp @@ -0,0 +1,47 @@ +#ifndef OPENMW_COMPONENTS_MULTIOBJECTCACHE_H +#define OPENMW_COMPONENTS_MULTIOBJECTCACHE_H + +#include +#include + +#include +#include + +namespace osg +{ + class Object; + class State; +} + +namespace Resource +{ + + /// @brief Cache for "non reusable" objects. + class MultiObjectCache : public osg::Referenced + { + public: + MultiObjectCache(); + ~MultiObjectCache(); + + void removeUnreferencedObjectsInCache(); + + void addEntryToObjectCache(const std::string& filename, osg::Object* object); + + /** Take an Object from cache. Return NULL if no object found. */ + osg::ref_ptr takeFromObjectCache(const std::string& fileName); + + /** call releaseGLObjects on all objects attached to the object cache.*/ + void releaseGLObjects(osg::State* state); + + protected: + + typedef std::multimap > ObjectCacheMap; + + ObjectCacheMap _objectCache; + OpenThreads::Mutex _objectCacheMutex; + + }; + +} + +#endif diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index f083e8175..5acd5f8fb 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -28,6 +28,7 @@ #include "imagemanager.hpp" #include "niffilemanager.hpp" #include "objectcache.hpp" +#include "multiobjectcache.hpp" namespace { @@ -233,6 +234,7 @@ namespace Resource SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) : ResourceManager(vfs) + , mInstanceCache(new MultiObjectCache) , mImageManager(imageManager) , mNifFileManager(nifFileManager) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) @@ -246,7 +248,6 @@ namespace Resource SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types - } /// @brief Callback to read image files from the VFS. @@ -372,7 +373,17 @@ namespace Resource } } - osg::ref_ptr SceneManager::createInstance(const std::string &name) + osg::ref_ptr SceneManager::cacheInstance(const std::string &name) + { + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr node = createInstance(normalized); + mInstanceCache->addEntryToObjectCache(normalized, node.get()); + return node; + } + + osg::ref_ptr SceneManager::createInstance(const std::string& name) { osg::ref_ptr scene = getTemplate(name); osg::ref_ptr cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); @@ -383,9 +394,22 @@ namespace Resource return cloned; } - osg::ref_ptr SceneManager::createInstance(const std::string &name, osg::Group* parentNode) + osg::ref_ptr SceneManager::getInstance(const std::string &name) { - osg::ref_ptr cloned = createInstance(name); + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr obj = mInstanceCache->takeFromObjectCache(normalized); + if (obj.get()) + return static_cast(obj.get()); + + return createInstance(normalized); + + } + + osg::ref_ptr SceneManager::getInstance(const std::string &name, osg::Group* parentNode) + { + osg::ref_ptr cloned = getInstance(name); attachTo(cloned, parentNode); return cloned; } @@ -487,4 +511,11 @@ namespace Resource mUnRefImageDataAfterApply = unref; } + void SceneManager::updateCache(double referenceTime) + { + ResourceManager::updateCache(referenceTime); + + mInstanceCache->removeUnreferencedObjectsInCache(); + } + } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 0345fff22..8357e63cd 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -29,6 +29,8 @@ namespace osgViewer namespace Resource { + class MultiObjectCache; + /// @brief Handles loading and caching of scenes, e.g. .nif files or .osg files /// @note Some methods of the scene manager can be used from any thread, see the methods documentation for more details. class SceneManager : public ResourceManager @@ -43,15 +45,21 @@ namespace Resource /// @note Thread safe. osg::ref_ptr getTemplate(const std::string& name); - /// Create an instance of the given scene template + /// Create an instance of the given scene template and cache it for later use, so that future calls to getInstance() can simply + /// return this cached object instead of creating a new one. + /// @note The returned ref_ptr may be kept around by the caller to ensure that the object stays in cache for as long as needed. + /// @note Thread safe. + osg::ref_ptr cacheInstance(const std::string& name); + + /// Get an instance of the given scene template /// @see getTemplate /// @note Thread safe. - osg::ref_ptr createInstance(const std::string& name); + osg::ref_ptr getInstance(const std::string& name); - /// Create an instance of the given scene template and immediately attach it to a parent node + /// Get an instance of the given scene template and immediately attach it to a parent node /// @see getTemplate /// @note Not thread safe, unless parentNode is not part of the main scene graph yet. - osg::ref_ptr createInstance(const std::string& name, osg::Group* parentNode); + osg::ref_ptr getInstance(const std::string& name, osg::Group* parentNode); /// Attach the given scene instance to the given parent node /// @note You should have the parentNode in its intended position before calling this method, @@ -88,7 +96,15 @@ namespace Resource /// otherwise should be disabled to reduce memory usage. void setUnRefImageDataAfterApply(bool unref); + /// @see ResourceManager::updateCache + virtual void updateCache(double referenceTime); + private: + + osg::ref_ptr createInstance(const std::string& name); + + osg::ref_ptr mInstanceCache; + OpenThreads::Mutex mSharedStateMutex; Resource::ImageManager* mImageManager;