Use a common base class for resource managers

Implement updateCache to delete unreferenced cached objects when they have not been referenced for a while.
This commit is contained in:
scrawl 2016-02-06 16:57:54 +01:00
parent ea1efaac0c
commit df57d4bfba
19 changed files with 152 additions and 105 deletions

View file

@ -640,12 +640,15 @@ namespace MWPhysics
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode) PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
: mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager())) : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager()))
, mResourceSystem(resourceSystem)
, mDebugDrawEnabled(false) , mDebugDrawEnabled(false)
, mTimeAccum(0.0f) , mTimeAccum(0.0f)
, mWaterHeight(0) , mWaterHeight(0)
, mWaterEnabled(false) , mWaterEnabled(false)
, mParentNode(parentNode) , mParentNode(parentNode)
{ {
mResourceSystem->addResourceManager(mShapeManager.get());
mCollisionConfiguration = new btDefaultCollisionConfiguration(); mCollisionConfiguration = new btDefaultCollisionConfiguration();
mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); mDispatcher = new btCollisionDispatcher(mCollisionConfiguration);
mBroadphase = new btDbvtBroadphase(); mBroadphase = new btDbvtBroadphase();
@ -659,6 +662,8 @@ namespace MWPhysics
PhysicsSystem::~PhysicsSystem() PhysicsSystem::~PhysicsSystem()
{ {
mResourceSystem->removeResourceManager(mShapeManager.get());
if (mWaterCollisionObject.get()) if (mWaterCollisionObject.get())
mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get()); mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get());

View file

@ -169,6 +169,7 @@ namespace MWPhysics
btCollisionWorld* mCollisionWorld; btCollisionWorld* mCollisionWorld;
std::auto_ptr<Resource::BulletShapeManager> mShapeManager; std::auto_ptr<Resource::BulletShapeManager> mShapeManager;
Resource::ResourceSystem* mResourceSystem;
typedef std::map<MWWorld::ConstPtr, Object*> ObjectMap; typedef std::map<MWWorld::ConstPtr, Object*> ObjectMap;
ObjectMap mObjects; ObjectMap mObjects;

View file

@ -233,7 +233,7 @@ namespace MWRender
void RenderingManager::clearCache() void RenderingManager::clearCache()
{ {
mResourceSystem->clearCache(); mResourceSystem->updateCache(mViewer->getFrameStamp()->getReferenceTime());
if (mTerrain.get()) if (mTerrain.get())
mTerrain->clearCache(); mTerrain->clearCache();
} }

View file

@ -41,7 +41,7 @@ add_component_dir (vfs
) )
add_component_dir (resource 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 add_component_dir (sceneutil

View file

@ -97,10 +97,9 @@ private:
}; };
BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager)
: mVFS(vfs) : ResourceManager(vfs)
, mSceneManager(sceneMgr) , mSceneManager(sceneMgr)
, mNifFileManager(nifFileManager) , mNifFileManager(nifFileManager)
, mCache(new osgDB::ObjectCache)
{ {
} }

View file

@ -7,16 +7,7 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include "bulletshape.hpp" #include "bulletshape.hpp"
#include "resourcemanager.hpp"
namespace VFS
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
namespace Resource namespace Resource
{ {
@ -29,7 +20,7 @@ namespace Resource
/// Handles loading, caching and "instancing" of bullet shapes. /// 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. /// 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. /// @note May be used from any thread.
class BulletShapeManager class BulletShapeManager : public ResourceManager
{ {
public: public:
BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager);
@ -38,11 +29,8 @@ namespace Resource
osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name); osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
private: private:
const VFS::Manager* mVFS;
SceneManager* mSceneManager; SceneManager* mSceneManager;
NifFileManager* mNifFileManager; NifFileManager* mNifFileManager;
osg::ref_ptr<osgDB::ObjectCache> mCache;
}; };
} }

View file

@ -42,8 +42,7 @@ namespace Resource
{ {
ImageManager::ImageManager(const VFS::Manager *vfs) ImageManager::ImageManager(const VFS::Manager *vfs)
: mVFS(vfs) : ResourceManager(vfs)
, mCache(new osgDB::ObjectCache)
, mWarningImage(createWarningImage()) , mWarningImage(createWarningImage())
, mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba"))
{ {

View file

@ -8,20 +8,16 @@
#include <osg/Image> #include <osg/Image>
#include <osg/Texture2D> #include <osg/Texture2D>
#include "resourcemanager.hpp"
namespace osgViewer namespace osgViewer
{ {
class Viewer; class Viewer;
} }
namespace VFS
{
class Manager;
}
namespace osgDB namespace osgDB
{ {
class Options; class Options;
class ObjectCache;
} }
namespace Resource namespace Resource
@ -29,7 +25,7 @@ namespace Resource
/// @brief Handles loading/caching of Images. /// @brief Handles loading/caching of Images.
/// @note May be used from any thread. /// @note May be used from any thread.
class ImageManager class ImageManager : public ResourceManager
{ {
public: public:
ImageManager(const VFS::Manager* vfs); ImageManager(const VFS::Manager* vfs);
@ -39,15 +35,9 @@ namespace Resource
/// Returns the dummy image if the given image is not found. /// Returns the dummy image if the given image is not found.
osg::ref_ptr<osg::Image> getImage(const std::string& filename); osg::ref_ptr<osg::Image> getImage(const std::string& filename);
const VFS::Manager* getVFS() { return mVFS; }
osg::Image* getWarningImage(); osg::Image* getWarningImage();
private: private:
const VFS::Manager* mVFS;
osg::ref_ptr<osgDB::ObjectCache> mCache;
osg::ref_ptr<osg::Image> mWarningImage; osg::ref_ptr<osg::Image> mWarningImage;
osg::ref_ptr<osgDB::Options> mOptions; osg::ref_ptr<osgDB::Options> mOptions;

View file

@ -9,8 +9,7 @@ namespace Resource
{ {
KeyframeManager::KeyframeManager(const VFS::Manager* vfs) KeyframeManager::KeyframeManager(const VFS::Manager* vfs)
: mCache(new osgDB::ObjectCache) : ResourceManager(vfs)
, mVFS(vfs)
{ {
} }

View file

@ -4,15 +4,7 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <string> #include <string>
namespace VFS #include "resourcemanager.hpp"
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
namespace NifOsg namespace NifOsg
{ {
@ -24,22 +16,15 @@ namespace Resource
/// @brief Managing of keyframe resources /// @brief Managing of keyframe resources
/// @note May be used from any thread. /// @note May be used from any thread.
class KeyframeManager class KeyframeManager : public ResourceManager
{ {
public: public:
KeyframeManager(const VFS::Manager* vfs); KeyframeManager(const VFS::Manager* vfs);
~KeyframeManager(); ~KeyframeManager();
void clearCache();
/// Retrieve a read-only keyframe resource by name (case-insensitive). /// Retrieve a read-only keyframe resource by name (case-insensitive).
/// @note Throws an exception if the resource is not found. /// @note Throws an exception if the resource is not found.
osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name); osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name);
private:
osg::ref_ptr<osgDB::ObjectCache> mCache;
const VFS::Manager* mVFS;
}; };
} }

View file

@ -29,9 +29,9 @@ namespace Resource
}; };
NifFileManager::NifFileManager(const VFS::Manager *vfs) 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() 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) Nif::NIFFilePtr NifFileManager::get(const std::string &name)
{ {

View file

@ -5,39 +5,23 @@
#include <components/nif/niffile.hpp> #include <components/nif/niffile.hpp>
namespace VFS #include "resourcemanager.hpp"
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
namespace Resource namespace Resource
{ {
/// @brief Handles caching of NIFFiles. /// @brief Handles caching of NIFFiles.
/// @note May be used from any thread. /// @note May be used from any thread.
class NifFileManager class NifFileManager : public ResourceManager
{ {
public: public:
NifFileManager(const VFS::Manager* vfs); NifFileManager(const VFS::Manager* vfs);
~NifFileManager(); ~NifFileManager();
void clearCache();
/// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet. /// 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 /// @note For performance reasons the NifFileManager does not handle case folding, needs
/// to be done in advance by other managers accessing the NifFileManager. /// to be done in advance by other managers accessing the NifFileManager.
Nif::NIFFilePtr get(const std::string& name); Nif::NIFFilePtr get(const std::string& name);
private:
// Use the osgDB::ObjectCache so objects are retrieved in thread safe way
osg::ref_ptr<osgDB::ObjectCache> mCache;
const VFS::Manager* mVFS;
}; };
} }

View file

@ -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;
}
}

View file

@ -0,0 +1,43 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_MANAGER_H
#define OPENMW_COMPONENTS_RESOURCE_MANAGER_H
#include <osg/ref_ptr>
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<osgDB::ObjectCache> mCache;
double mExpiryDelay;
};
}
#endif

View file

@ -15,11 +15,21 @@ namespace Resource
mKeyframeManager.reset(new KeyframeManager(vfs)); mKeyframeManager.reset(new KeyframeManager(vfs));
mImageManager.reset(new ImageManager(vfs)); mImageManager.reset(new ImageManager(vfs));
mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get())); 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() ResourceSystem::~ResourceSystem()
{ {
// this has to be defined in the .cpp file as we can't delete incomplete types // 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() SceneManager* ResourceSystem::getSceneManager()
@ -42,9 +52,24 @@ namespace Resource
return mKeyframeManager.get(); return mKeyframeManager.get();
} }
void ResourceSystem::clearCache() void ResourceSystem::updateCache(double referenceTime)
{ {
mNifFileManager->clearCache(); osg::Timer timer;
for (std::vector<ResourceManager*>::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)
{
std::vector<ResourceManager*>::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr);
if (found != mResourceManagers.end())
mResourceManagers.erase(found);
} }
const VFS::Manager* ResourceSystem::getVFS() const const VFS::Manager* ResourceSystem::getVFS() const

View file

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H #define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H
#include <memory> #include <memory>
#include <vector>
namespace VFS namespace VFS
{ {
@ -15,6 +16,7 @@ namespace Resource
class ImageManager; class ImageManager;
class NifFileManager; class NifFileManager;
class KeyframeManager; class KeyframeManager;
class ResourceManager;
/// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems. /// @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 /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but
@ -31,8 +33,17 @@ namespace Resource
KeyframeManager* getKeyframeManager(); KeyframeManager* getKeyframeManager();
/// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced. /// 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; const VFS::Manager* getVFS() const;
private: private:
@ -41,6 +52,10 @@ namespace Resource
std::auto_ptr<NifFileManager> mNifFileManager; std::auto_ptr<NifFileManager> mNifFileManager;
std::auto_ptr<KeyframeManager> mKeyframeManager; std::auto_ptr<KeyframeManager> 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<ResourceManager*> mResourceManagers;
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
ResourceSystem(const ResourceSystem&); ResourceSystem(const ResourceSystem&);

View file

@ -230,7 +230,7 @@ namespace Resource
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
: mVFS(vfs) : ResourceManager(vfs)
, mImageManager(imageManager) , mImageManager(imageManager)
, mNifFileManager(nifFileManager) , mNifFileManager(nifFileManager)
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
@ -238,7 +238,6 @@ namespace Resource
, mMaxAnisotropy(1) , mMaxAnisotropy(1)
, mUnRefImageDataAfterApply(false) , mUnRefImageDataAfterApply(false)
, mParticleSystemMask(~0u) , mParticleSystemMask(~0u)
, mCache(new osgDB::ObjectCache)
{ {
} }
@ -403,11 +402,6 @@ namespace Resource
node->accept(visitor); node->accept(visitor);
} }
const VFS::Manager* SceneManager::getVFS() const
{
return mVFS;
}
Resource::ImageManager* SceneManager::getImageManager() Resource::ImageManager* SceneManager::getImageManager()
{ {
return mImageManager; return mImageManager;
@ -483,5 +477,4 @@ namespace Resource
mUnRefImageDataAfterApply = unref; mUnRefImageDataAfterApply = unref;
} }
} }

View file

@ -8,27 +8,19 @@
#include <osg/Node> #include <osg/Node>
#include <osg/Texture> #include <osg/Texture>
#include "resourcemanager.hpp"
namespace Resource namespace Resource
{ {
class ImageManager; class ImageManager;
class NifFileManager; class NifFileManager;
} }
namespace VFS
{
class Manager;
}
namespace osgUtil namespace osgUtil
{ {
class IncrementalCompileOperation; class IncrementalCompileOperation;
} }
namespace osgDB
{
class ObjectCache;
}
namespace osgViewer namespace osgViewer
{ {
class Viewer; class Viewer;
@ -39,7 +31,7 @@ namespace Resource
/// @brief Handles loading and caching of scenes, e.g. .nif files or .osg files /// @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. /// @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: public:
SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager); 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 /// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching
void notifyAttached(osg::Node* node) const; void notifyAttached(osg::Node* node) const;
const VFS::Manager* getVFS() const;
Resource::ImageManager* getImageManager(); Resource::ImageManager* getImageManager();
/// @param mask The node mask to apply to loaded particle system nodes. /// @param mask The node mask to apply to loaded particle system nodes.
@ -99,7 +89,6 @@ namespace Resource
void setUnRefImageDataAfterApply(bool unref); void setUnRefImageDataAfterApply(bool unref);
private: private:
const VFS::Manager* mVFS;
Resource::ImageManager* mImageManager; Resource::ImageManager* mImageManager;
Resource::NifFileManager* mNifFileManager; Resource::NifFileManager* mNifFileManager;
@ -112,8 +101,6 @@ namespace Resource
unsigned int mParticleSystemMask; unsigned int mParticleSystemMask;
osg::ref_ptr<osgDB::ObjectCache> mCache;
SceneManager(const SceneManager&); SceneManager(const SceneManager&);
void operator = (const SceneManager&); void operator = (const SceneManager&);
}; };

View file

@ -16,6 +16,7 @@ namespace VFS
/// @par Various archive types (e.g. directories on the filesystem, or compressed archives) /// @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 /// 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. /// 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 class Manager
{ {
public: public:
@ -33,20 +34,25 @@ namespace VFS
void buildIndex(); void buildIndex();
/// Does a file with this name exist? /// 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; bool exists(const std::string& name) const;
/// Get a complete list of files from all archives /// 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<std::string, File*>& getIndex() const; const std::map<std::string, File*>& getIndex() const;
/// Normalize the given filename, making slashes/backslashes consistent, and lower-casing if mStrict is false. /// 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; void normalizeFilename(std::string& name) const;
/// Retrieve a file by name. /// Retrieve a file by name.
/// @note Throws an exception if the file can not be found. /// @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; Files::IStreamPtr get(const std::string& name) const;
/// Retrieve a file by name (name is already normalized). /// Retrieve a file by name (name is already normalized).
/// @note Throws an exception if the file can not be found. /// @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; Files::IStreamPtr getNormalized(const std::string& normalizedName) const;
private: private: