diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index c99456ae52..11d6d286a4 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -640,12 +640,15 @@ namespace MWPhysics PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager())) + , mResourceSystem(resourceSystem) , mDebugDrawEnabled(false) , mTimeAccum(0.0f) , mWaterHeight(0) , mWaterEnabled(false) , mParentNode(parentNode) { + mResourceSystem->addResourceManager(mShapeManager.get()); + mCollisionConfiguration = new btDefaultCollisionConfiguration(); mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); mBroadphase = new btDbvtBroadphase(); @@ -659,6 +662,8 @@ namespace MWPhysics PhysicsSystem::~PhysicsSystem() { + mResourceSystem->removeResourceManager(mShapeManager.get()); + if (mWaterCollisionObject.get()) mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get()); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index f53d7e3d9b..4263bc0ec3 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -169,6 +169,7 @@ namespace MWPhysics btCollisionWorld* mCollisionWorld; std::auto_ptr mShapeManager; + Resource::ResourceSystem* mResourceSystem; typedef std::map ObjectMap; ObjectMap mObjects; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9b7ced4b58..d4199d5f6c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -233,7 +233,7 @@ namespace MWRender void RenderingManager::clearCache() { - mResourceSystem->clearCache(); + mResourceSystem->updateCache(mViewer->getFrameStamp()->getReferenceTime()); if (mTerrain.get()) mTerrain->clearCache(); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0b54ddeb73..42323a68a0 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -41,7 +41,7 @@ add_component_dir (vfs ) add_component_dir (resource - scenemanager keyframemanager imagemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache + scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache resourcesystem resourcemanager ) add_component_dir (sceneutil diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 2ab7b243aa..b5581cce24 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -97,10 +97,9 @@ private: }; BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) - : mVFS(vfs) + : ResourceManager(vfs) , mSceneManager(sceneMgr) , mNifFileManager(nifFileManager) - , mCache(new osgDB::ObjectCache) { } diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index c8db8849e7..576268f753 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -7,16 +7,7 @@ #include #include "bulletshape.hpp" - -namespace VFS -{ - class Manager; -} - -namespace osgDB -{ - class ObjectCache; -} +#include "resourcemanager.hpp" namespace Resource { @@ -29,7 +20,7 @@ namespace Resource /// Handles loading, caching and "instancing" of bullet shapes. /// A shape 'instance' is a clone of another shape, with the goal of setting a different scale on this instance. /// @note May be used from any thread. - class BulletShapeManager + class BulletShapeManager : public ResourceManager { public: BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); @@ -38,11 +29,8 @@ namespace Resource osg::ref_ptr createInstance(const std::string& name); private: - const VFS::Manager* mVFS; SceneManager* mSceneManager; NifFileManager* mNifFileManager; - - osg::ref_ptr mCache; }; } diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index bd422fe2a6..79da7d7abf 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -42,8 +42,7 @@ namespace Resource { ImageManager::ImageManager(const VFS::Manager *vfs) - : mVFS(vfs) - , mCache(new osgDB::ObjectCache) + : ResourceManager(vfs) , mWarningImage(createWarningImage()) , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) { diff --git a/components/resource/imagemanager.hpp b/components/resource/imagemanager.hpp index bc1d7b4e4b..8d9ad2c327 100644 --- a/components/resource/imagemanager.hpp +++ b/components/resource/imagemanager.hpp @@ -8,20 +8,16 @@ #include #include +#include "resourcemanager.hpp" + namespace osgViewer { class Viewer; } -namespace VFS -{ - class Manager; -} - namespace osgDB { class Options; - class ObjectCache; } namespace Resource @@ -29,7 +25,7 @@ namespace Resource /// @brief Handles loading/caching of Images. /// @note May be used from any thread. - class ImageManager + class ImageManager : public ResourceManager { public: ImageManager(const VFS::Manager* vfs); @@ -39,15 +35,9 @@ namespace Resource /// Returns the dummy image if the given image is not found. osg::ref_ptr getImage(const std::string& filename); - const VFS::Manager* getVFS() { return mVFS; } - osg::Image* getWarningImage(); private: - const VFS::Manager* mVFS; - - osg::ref_ptr mCache; - osg::ref_ptr mWarningImage; osg::ref_ptr mOptions; diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 7e948dcb03..4392f84c19 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -9,8 +9,7 @@ namespace Resource { KeyframeManager::KeyframeManager(const VFS::Manager* vfs) - : mCache(new osgDB::ObjectCache) - , mVFS(vfs) + : ResourceManager(vfs) { } diff --git a/components/resource/keyframemanager.hpp b/components/resource/keyframemanager.hpp index 5a5cb36284..1c2c219bb1 100644 --- a/components/resource/keyframemanager.hpp +++ b/components/resource/keyframemanager.hpp @@ -4,15 +4,7 @@ #include #include -namespace VFS -{ - class Manager; -} - -namespace osgDB -{ - class ObjectCache; -} +#include "resourcemanager.hpp" namespace NifOsg { @@ -24,22 +16,15 @@ namespace Resource /// @brief Managing of keyframe resources /// @note May be used from any thread. - class KeyframeManager + class KeyframeManager : public ResourceManager { public: KeyframeManager(const VFS::Manager* vfs); ~KeyframeManager(); - void clearCache(); - /// Retrieve a read-only keyframe resource by name (case-insensitive). /// @note Throws an exception if the resource is not found. osg::ref_ptr get(const std::string& name); - - private: - osg::ref_ptr mCache; - - const VFS::Manager* mVFS; }; } diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp index 1d8019b69d..3c7437520b 100644 --- a/components/resource/niffilemanager.cpp +++ b/components/resource/niffilemanager.cpp @@ -29,9 +29,9 @@ namespace Resource }; NifFileManager::NifFileManager(const VFS::Manager *vfs) - : mVFS(vfs) + : ResourceManager(vfs, 0.0) // NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager, + // so we'll use expiryDelay of 0 to instantly delete NIF files after use. { - mCache = new osgDB::ObjectCache; } NifFileManager::~NifFileManager() @@ -39,12 +39,6 @@ namespace Resource } - void NifFileManager::clearCache() - { - // NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager, - // so we'll simply drop all nif files here, unlikely to need them again - mCache->clear(); - } Nif::NIFFilePtr NifFileManager::get(const std::string &name) { diff --git a/components/resource/niffilemanager.hpp b/components/resource/niffilemanager.hpp index 4551cf2275..4b43ff24bf 100644 --- a/components/resource/niffilemanager.hpp +++ b/components/resource/niffilemanager.hpp @@ -5,39 +5,23 @@ #include -namespace VFS -{ - class Manager; -} - -namespace osgDB -{ - class ObjectCache; -} +#include "resourcemanager.hpp" namespace Resource { /// @brief Handles caching of NIFFiles. /// @note May be used from any thread. - class NifFileManager + class NifFileManager : public ResourceManager { public: NifFileManager(const VFS::Manager* vfs); ~NifFileManager(); - void clearCache(); - /// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet. /// @note For performance reasons the NifFileManager does not handle case folding, needs /// to be done in advance by other managers accessing the NifFileManager. Nif::NIFFilePtr get(const std::string& name); - - private: - // Use the osgDB::ObjectCache so objects are retrieved in thread safe way - osg::ref_ptr mCache; - - const VFS::Manager* mVFS; }; } diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp new file mode 100644 index 0000000000..60233baa0d --- /dev/null +++ b/components/resource/resourcemanager.cpp @@ -0,0 +1,34 @@ +#include "resourcemanager.hpp" + +#include "objectcache.hpp" + +namespace Resource +{ + + ResourceManager::ResourceManager(const VFS::Manager *vfs, const double expiryDelay) + : mVFS(vfs) + , mCache(new osgDB::ObjectCache) + , mExpiryDelay(expiryDelay) + { + + } + + void ResourceManager::updateCache(double referenceTime) + { + // NOTE: we could clear the cache from the background thread if the deletion proves too much of an overhead + // idea: customize objectCache to not hold a lock while doing the actual deletion + mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime); + mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay); + } + + void ResourceManager::clearCache() + { + mCache->clear(); + } + + const VFS::Manager* ResourceManager::getVFS() const + { + return mVFS; + } + +} diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp new file mode 100644 index 0000000000..e45a2e9cb7 --- /dev/null +++ b/components/resource/resourcemanager.hpp @@ -0,0 +1,43 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_MANAGER_H +#define OPENMW_COMPONENTS_RESOURCE_MANAGER_H + +#include + +namespace VFS +{ + class Manager; +} + +namespace osgDB +{ + class ObjectCache; +} + +namespace Resource +{ + + /// @brief Base class for managers that require a virtual file system and object cache. + /// @par This base class implements clearing of the cache, but populating it and what it's used for is up to the individual sub classes. + class ResourceManager + { + public: + /// @param expiryDelay how long to keep objects in cache after no longer being referenced. + ResourceManager(const VFS::Manager* vfs, const double expiryDelay = 300.0); + + /// Clear cache entries that have not been referenced for longer than expiryDelay. + virtual void updateCache(double referenceTime); + + /// Clear all cache entries regardless of having external references. + virtual void clearCache(); + + const VFS::Manager* getVFS() const; + + protected: + const VFS::Manager* mVFS; + osg::ref_ptr mCache; + double mExpiryDelay; + }; + +} + +#endif diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index d39a723d6b..f499c0016b 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -15,11 +15,21 @@ namespace Resource mKeyframeManager.reset(new KeyframeManager(vfs)); mImageManager.reset(new ImageManager(vfs)); mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get())); + + addResourceManager(mNifFileManager.get()); + addResourceManager(mKeyframeManager.get()); + // note, scene references images so add images afterwards for correct implementation of updateCache() + addResourceManager(mSceneManager.get()); + addResourceManager(mImageManager.get()); } ResourceSystem::~ResourceSystem() { // this has to be defined in the .cpp file as we can't delete incomplete types + + mResourceManagers.clear(); + + // no delete, all handled by auto_ptr } SceneManager* ResourceSystem::getSceneManager() @@ -42,9 +52,24 @@ namespace Resource return mKeyframeManager.get(); } - void ResourceSystem::clearCache() + void ResourceSystem::updateCache(double referenceTime) + { + osg::Timer timer; + for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + (*it)->updateCache(referenceTime); + std::cout << "updateCache took " << timer.time_m() << " ms" << std::endl; + } + + void ResourceSystem::addResourceManager(ResourceManager *resourceMgr) + { + mResourceManagers.push_back(resourceMgr); + } + + void ResourceSystem::removeResourceManager(ResourceManager *resourceMgr) { - mNifFileManager->clearCache(); + std::vector::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr); + if (found != mResourceManagers.end()) + mResourceManagers.erase(found); } const VFS::Manager* ResourceSystem::getVFS() const diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 13a96e8c7f..30607432da 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H #include +#include namespace VFS { @@ -15,6 +16,7 @@ namespace Resource class ImageManager; class NifFileManager; class KeyframeManager; + class ResourceManager; /// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems. /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but @@ -31,8 +33,17 @@ namespace Resource KeyframeManager* getKeyframeManager(); /// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced. - void clearCache(); + /// @note May be called from any thread if you do not add or remove resource managers at that point. + void updateCache(double referenceTime); + /// Add this ResourceManager to be handled by the ResourceSystem. + /// @note Does not transfer ownership. + void addResourceManager(ResourceManager* resourceMgr); + /// @note Do nothing if resourceMgr does not exist. + /// @note Does not delete resourceMgr. + void removeResourceManager(ResourceManager* resourceMgr); + + /// @note May be called from any thread. const VFS::Manager* getVFS() const; private: @@ -41,6 +52,10 @@ namespace Resource std::auto_ptr mNifFileManager; std::auto_ptr mKeyframeManager; + // Store the base classes separately to get convenient access to the common interface + // Here users can register their own resourcemanager as well + std::vector mResourceManagers; + const VFS::Manager* mVFS; ResourceSystem(const ResourceSystem&); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 95e03b3894..4ed14241a2 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -230,7 +230,7 @@ namespace Resource SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) - : mVFS(vfs) + : ResourceManager(vfs) , mImageManager(imageManager) , mNifFileManager(nifFileManager) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) @@ -238,7 +238,6 @@ namespace Resource , mMaxAnisotropy(1) , mUnRefImageDataAfterApply(false) , mParticleSystemMask(~0u) - , mCache(new osgDB::ObjectCache) { } @@ -403,11 +402,6 @@ namespace Resource node->accept(visitor); } - const VFS::Manager* SceneManager::getVFS() const - { - return mVFS; - } - Resource::ImageManager* SceneManager::getImageManager() { return mImageManager; @@ -483,5 +477,4 @@ namespace Resource mUnRefImageDataAfterApply = unref; } - } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 32bd806603..173131e666 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -8,27 +8,19 @@ #include #include +#include "resourcemanager.hpp" + namespace Resource { class ImageManager; class NifFileManager; } -namespace VFS -{ - class Manager; -} - namespace osgUtil { class IncrementalCompileOperation; } -namespace osgDB -{ - class ObjectCache; -} - namespace osgViewer { class Viewer; @@ -39,7 +31,7 @@ namespace Resource /// @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 + class SceneManager : public ResourceManager { public: SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager); @@ -78,8 +70,6 @@ namespace Resource /// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching void notifyAttached(osg::Node* node) const; - const VFS::Manager* getVFS() const; - Resource::ImageManager* getImageManager(); /// @param mask The node mask to apply to loaded particle system nodes. @@ -99,7 +89,6 @@ namespace Resource void setUnRefImageDataAfterApply(bool unref); private: - const VFS::Manager* mVFS; Resource::ImageManager* mImageManager; Resource::NifFileManager* mNifFileManager; @@ -112,8 +101,6 @@ namespace Resource unsigned int mParticleSystemMask; - osg::ref_ptr mCache; - SceneManager(const SceneManager&); void operator = (const SceneManager&); }; diff --git a/components/vfs/manager.hpp b/components/vfs/manager.hpp index f74914977a..6592a65a88 100644 --- a/components/vfs/manager.hpp +++ b/components/vfs/manager.hpp @@ -16,6 +16,7 @@ namespace VFS /// @par Various archive types (e.g. directories on the filesystem, or compressed archives) /// can be registered, and will be merged into a single file tree. If the same filename is /// contained in multiple archives, the last added archive will have priority. + /// @par Most of the methods in this class are considered thread-safe, see each method documentation for details. class Manager { public: @@ -33,20 +34,25 @@ namespace VFS void buildIndex(); /// Does a file with this name exist? + /// @note May be called from any thread once the index has been built. bool exists(const std::string& name) const; /// Get a complete list of files from all archives + /// @note May be called from any thread once the index has been built. const std::map& getIndex() const; /// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false. + /// @note May be called from any thread once the index has been built. void normalizeFilename(std::string& name) const; /// Retrieve a file by name. /// @note Throws an exception if the file can not be found. + /// @note May be called from any thread once the index has been built. Files::IStreamPtr get(const std::string& name) const; /// Retrieve a file by name (name is already normalized). /// @note Throws an exception if the file can not be found. + /// @note May be called from any thread once the index has been built. Files::IStreamPtr getNormalized(const std::string& normalizedName) const; private: