diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 1f59c5c99..db010d0fb 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -636,7 +636,7 @@ namespace MWPhysics // --------------------------------------------------------------- PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) - : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager())) + : mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager())) , mDebugDrawEnabled(false) , mTimeAccum(0.0f) , mWaterHeight(0) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 5598598d0..c6b50aae3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -403,6 +403,8 @@ namespace MWWorld // Delay the map update until scripts have been given a chance to run. // If we don't do this, objects that should be disabled will still appear on the map. mNeedMapUpdate = true; + + mRendering.getResourceSystem()->clearCache(); } void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) @@ -518,6 +520,8 @@ namespace MWWorld // Delay the map update until scripts have been given a chance to run. // If we don't do this, objects that should be disabled will still appear on the map. mNeedMapUpdate = true; + + mRendering.getResourceSystem()->clearCache(); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0f2906ce5..3e4225d7c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -37,7 +37,7 @@ add_component_dir (vfs ) add_component_dir (resource - scenemanager texturemanager resourcesystem bulletshapemanager bulletshape + scenemanager texturemanager resourcesystem bulletshapemanager bulletshape niffilemanager ) add_component_dir (sceneutil diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 9aae1cad4..593322190 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -12,6 +12,7 @@ #include "bulletshape.hpp" #include "scenemanager.hpp" +#include "niffilemanager.hpp" namespace Resource @@ -94,9 +95,10 @@ private: btTriangleMesh* mTriangleMesh; }; -BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr) +BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) : mVFS(vfs) , mSceneManager(sceneMgr) + , mNifFileManager(nifFileManager) { } @@ -115,8 +117,6 @@ osg::ref_ptr BulletShapeManager::createInstance(const std:: Index::iterator it = mIndex.find(normalized); if (it == mIndex.end()) { - Files::IStreamPtr file = mVFS->get(normalized); - size_t extPos = normalized.find_last_of('.'); std::string ext; if (extPos != std::string::npos && extPos+1 < normalized.size()) @@ -126,7 +126,7 @@ osg::ref_ptr BulletShapeManager::createInstance(const std:: { NifBullet::BulletNifLoader loader; // might be worth sharing NIFFiles with SceneManager in some way - shape = loader.load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalized))); + shape = loader.load(mNifFileManager->get(normalized)); } else { diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index 6b8e64c21..ac1523495 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -16,6 +16,7 @@ namespace VFS namespace Resource { class SceneManager; + class NifFileManager; class BulletShape; class BulletShapeInstance; @@ -23,7 +24,7 @@ namespace Resource class BulletShapeManager { public: - BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr); + BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); ~BulletShapeManager(); osg::ref_ptr createInstance(const std::string& name); @@ -31,6 +32,7 @@ namespace Resource private: const VFS::Manager* mVFS; SceneManager* mSceneManager; + NifFileManager* mNifFileManager; typedef std::map > Index; Index mIndex; diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp new file mode 100644 index 000000000..9e426bf60 --- /dev/null +++ b/components/resource/niffilemanager.cpp @@ -0,0 +1,64 @@ +#include "niffilemanager.hpp" + +#include +#include + +#include + +namespace Resource +{ + + class NifFileHolder : public osg::Object + { + public: + NifFileHolder(const Nif::NIFFilePtr& file) + : mNifFile(file) + { + } + NifFileHolder(const NifFileHolder& copy, const osg::CopyOp& copyop) + : mNifFile(copy.mNifFile) + { + } + + NifFileHolder() + { + } + + META_Object(Resource, NifFileHolder) + + Nif::NIFFilePtr mNifFile; + }; + + NifFileManager::NifFileManager(const VFS::Manager *vfs) + : mVFS(vfs) + { + mCache = new osgDB::ObjectCache; + } + + NifFileManager::~NifFileManager() + { + + } + + 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) + { + osg::ref_ptr obj = mCache->getRefFromObjectCache(name); + if (obj) + return static_cast(obj.get())->mNifFile; + else + { + Nif::NIFFilePtr file (new Nif::NIFFile(mVFS->get(name), name)); + obj = new NifFileHolder(file); + mCache->addEntryToObjectCache(name, obj); + return file; + } + } + +} diff --git a/components/resource/niffilemanager.hpp b/components/resource/niffilemanager.hpp new file mode 100644 index 000000000..90ad9fc29 --- /dev/null +++ b/components/resource/niffilemanager.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H +#define OPENMW_COMPONENTS_RESOURCE_NIFFILEMANAGER_H + +#include + +#include + +namespace VFS +{ + class Manager; +} + +namespace osgDB +{ + class ObjectCache; +} + +namespace Resource +{ + + /// @brief Handles caching of NIFFiles. + /// @note The NifFileManager is completely thread safe. + class NifFileManager + { + 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; + }; + +} + +#endif diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index bd6824079..2dfd30314 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -2,6 +2,7 @@ #include "scenemanager.hpp" #include "texturemanager.hpp" +#include "niffilemanager.hpp" namespace Resource { @@ -9,8 +10,9 @@ namespace Resource ResourceSystem::ResourceSystem(const VFS::Manager *vfs) : mVFS(vfs) { + mNifFileManager.reset(new NifFileManager(vfs)); mTextureManager.reset(new TextureManager(vfs)); - mSceneManager.reset(new SceneManager(vfs, mTextureManager.get())); + mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get())); } ResourceSystem::~ResourceSystem() @@ -28,6 +30,16 @@ namespace Resource return mTextureManager.get(); } + NifFileManager *ResourceSystem::getNifFileManager() + { + return mNifFileManager.get(); + } + + void ResourceSystem::clearCache() + { + mNifFileManager->clearCache(); + } + const VFS::Manager* ResourceSystem::getVFS() const { return mVFS; diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 7c00a11ee..7f90bff27 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -13,8 +13,9 @@ namespace Resource class SceneManager; class TextureManager; + class NifFileManager; - /// @brief Wrapper class that constructs and provides access to the various 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 /// are built around the use of a single virtual file system. class ResourceSystem @@ -25,12 +26,17 @@ namespace Resource SceneManager* getSceneManager(); TextureManager* getTextureManager(); + NifFileManager* getNifFileManager(); + + /// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced. + void clearCache(); const VFS::Manager* getVFS() const; private: std::auto_ptr mSceneManager; std::auto_ptr mTextureManager; + std::auto_ptr mNifFileManager; const VFS::Manager* mVFS; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index b2ba5c4cf..3ee7a8c00 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -21,6 +21,7 @@ #include #include "texturemanager.hpp" +#include "niffilemanager.hpp" namespace { @@ -104,9 +105,10 @@ namespace namespace Resource { - SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager) + SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) , mTextureManager(textureManager) + , mNifFileManager(nifFileManager) , mParticleSystemMask(~0u) { } @@ -150,11 +152,11 @@ namespace Resource return std::string(); } - osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::TextureManager* textureMgr) + osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::TextureManager* textureMgr, Resource::NifFileManager* nifFileManager) { std::string ext = getFileExtension(normalizedFilename); if (ext == "nif") - return NifOsg::Loader::load(Nif::NIFFilePtr(new Nif::NIFFile(file, normalizedFilename)), textureMgr); + return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), textureMgr); else { osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); @@ -195,14 +197,14 @@ namespace Resource { Files::IStreamPtr file = mVFS->get(normalized); - loaded = load(file, normalized, mTextureManager); + loaded = load(file, normalized, mTextureManager, mNifFileManager); } catch (std::exception& e) { std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error.nif instead" << std::endl; Files::IStreamPtr file = mVFS->get("meshes/marker_error.nif"); normalized = "meshes/marker_error.nif"; - loaded = load(file, normalized, mTextureManager); + loaded = load(file, normalized, mTextureManager, mNifFileManager); } osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 5ecb875ac..3c1984fd9 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -10,6 +10,7 @@ namespace Resource { class TextureManager; + class NifFileManager; } namespace VFS @@ -34,7 +35,7 @@ namespace Resource class SceneManager { public: - SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager); + SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager); ~SceneManager(); /// Get a read-only copy of this scene "template" @@ -66,7 +67,7 @@ namespace Resource /// Set up an IncrementalCompileOperation for background compiling of loaded scenes. void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico); - /// @note If you used SceneManager::attachTo, this was called automatically. + /// @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; @@ -79,6 +80,7 @@ namespace Resource private: const VFS::Manager* mVFS; Resource::TextureManager* mTextureManager; + Resource::NifFileManager* mNifFileManager; osg::ref_ptr mIncrementalCompileOperation;