From 5b972ee7776e198c374f8ff76d3cae1682175397 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 18:51:51 +0100 Subject: [PATCH 01/66] Move texture filtering settings to SceneManager Practical benefits: - Filter settings are now applied to native OSG format models. These models do not use TextureManager::getTexture2D since the model itself specifies a Texture. - The GUI render manager will be able to use its own separate textures, making it easier to turn off filtering for them. --- apps/openmw/engine.cpp | 6 +- apps/openmw/mwrender/animation.cpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 2 +- components/nifosg/controller.hpp | 2 + components/resource/scenemanager.cpp | 207 ++++++++++++++++++++++ components/resource/scenemanager.hpp | 31 ++++ components/resource/texturemanager.cpp | 92 ---------- components/resource/texturemanager.hpp | 17 -- 8 files changed, 247 insertions(+), 114 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f347bc350..d96bf16e4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include @@ -448,8 +448,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get())); - mResourceSystem->getTextureManager()->setUnRefImageDataAfterApply(true); - mResourceSystem->getTextureManager()->setFilterSettings( + mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(true); + mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture mipmap", "General"), diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 549d0eb8e..a7bb3f128 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1059,7 +1059,9 @@ namespace MWRender stream << i; stream << ".dds"; - textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT)); + osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT); + mResourceSystem->getSceneManager()->applyFilterSettings(tex); + textures.push_back(tex); } osg::ref_ptr glowupdater (new GlowUpdater(glowColor, textures)); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c8d0925b0..755a8019b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -776,7 +776,7 @@ namespace MWRender void RenderingManager::updateTextureFiltering() { - mResourceSystem->getTextureManager()->setFilterSettings( + mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture mipmap", "General"), diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index e1e969d06..0b633a122 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -305,6 +305,8 @@ namespace NifOsg META_Object(NifOsg, FlipController) + std::vector >& getTextures() { return mTextures; } + virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); }; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 43ece41f3..80e6f737d 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -9,6 +9,8 @@ #include +#include + #include #include @@ -19,6 +21,7 @@ #include #include +#include #include "texturemanager.hpp" #include "niffilemanager.hpp" @@ -105,10 +108,123 @@ namespace namespace Resource { + /// Set texture filtering settings on textures contained in a FlipController. + class SetFilterSettingsControllerVisitor : public SceneUtil::ControllerVisitor + { + public: + SetFilterSettingsControllerVisitor(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) + : mMinFilter(minFilter) + , mMagFilter(magFilter) + , mMaxAnisotropy(maxAnisotropy) + { + } + + virtual void visit(osg::Node& node, SceneUtil::Controller& ctrl) + { + if (NifOsg::FlipController* flipctrl = dynamic_cast(&ctrl)) + { + for (std::vector >::iterator it = flipctrl->getTextures().begin(); it != flipctrl->getTextures().end(); ++it) + { + osg::Texture* tex = *it; + tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + tex->setMaxAnisotropy(mMaxAnisotropy); + mTexturesProcessed.insert(tex); + } + } + } + + const std::set >& getTexturesProcessed() + { + return mTexturesProcessed; + } + + private: + osg::Texture::FilterMode mMinFilter; + osg::Texture::FilterMode mMagFilter; + int mMaxAnisotropy; + std::set > mTexturesProcessed; + }; + + /// Set texture filtering settings on textures contained in StateSets. + class SetFilterSettingsVisitor : public osg::NodeVisitor + { + public: + SetFilterSettingsVisitor(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mMinFilter(minFilter) + , mMagFilter(magFilter) + , mMaxAnisotropy(maxAnisotropy) + { + } + + virtual void apply(osg::Node& node) + { + osg::StateSet* stateset = node.getStateSet(); + if (stateset) + apply(stateset); + traverse(node); + } + + virtual void apply(osg::Geode& geode) + { + osg::StateSet* stateset = geode.getStateSet(); + if (stateset) + apply(stateset); + + for (unsigned int i=0; igetStateSet(); + if (stateset) + apply(stateset); + } + } + + void apply(osg::StateSet* stateset) + { + const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); + for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); + apply(texture); + } + } + + void apply(osg::StateAttribute* attr) + { + osg::Texture* tex = attr->asTexture(); + if (tex) + { + tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + tex->setMaxAnisotropy(mMaxAnisotropy); + mTexturesProcessed.insert(tex); + } + } + + const std::set >& getTexturesProcessed() + { + return mTexturesProcessed; + } + + private: + osg::Texture::FilterMode mMinFilter; + osg::Texture::FilterMode mMagFilter; + int mMaxAnisotropy; + std::set > mTexturesProcessed; + }; + + + SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) , mTextureManager(textureManager) , mNifFileManager(nifFileManager) + , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) + , mMagFilter(osg::Texture::LINEAR) + , mMaxAnisotropy(1) + , mUnRefImageDataAfterApply(false) , mParticleSystemMask(~0u) { } @@ -219,6 +335,17 @@ namespace Resource throw; } + // set filtering settings + SetFilterSettingsVisitor setFilterSettingsVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); + loaded->accept(setFilterSettingsVisitor); + SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); + loaded->accept(setFilterSettingsControllerVisitor); + + // remember which textures we set a filtering setting on so we can re-apply it when the setting changes + mTexturesWithFilterSetting.insert(setFilterSettingsVisitor.getTexturesProcessed().begin(), setFilterSettingsVisitor.getTexturesProcessed().end()); + mTexturesWithFilterSetting.insert(setFilterSettingsControllerVisitor.getTexturesProcessed().begin(), setFilterSettingsControllerVisitor.getTexturesProcessed().end()); + + // share state osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); if (mIncrementalCompileOperation) @@ -285,4 +412,84 @@ namespace Resource mParticleSystemMask = mask; } + void SceneManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, + const std::string &mipmap, int maxAnisotropy, + osgViewer::Viewer *viewer) + { + osg::Texture::FilterMode min = osg::Texture::LINEAR; + osg::Texture::FilterMode mag = osg::Texture::LINEAR; + + if(magfilter == "nearest") + mag = osg::Texture::NEAREST; + else if(magfilter != "linear") + std::cerr<< "Invalid texture mag filter: "<stopThreading(); + setFilterSettings(min, mag, maxAnisotropy); + if(viewer) viewer->startThreading(); + } + + void SceneManager::applyFilterSettings(osg::Texture *tex) + { + tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + tex->setMaxAnisotropy(mMaxAnisotropy); + mTexturesWithFilterSetting.insert(tex); + } + + void SceneManager::setUnRefImageDataAfterApply(bool unref) + { + mUnRefImageDataAfterApply = unref; + } + + void SceneManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) + { + mMinFilter = minFilter; + mMagFilter = magFilter; + mMaxAnisotropy = std::max(1, maxAnisotropy); + + for (std::set >::const_iterator it = mTexturesWithFilterSetting.begin(); it != mTexturesWithFilterSetting.end(); ++it) + { + (*it)->setFilter(osg::Texture::MIN_FILTER, mMinFilter); + (*it)->setFilter(osg::Texture::MAG_FILTER, mMagFilter); + (*it)->setMaxAnisotropy(mMaxAnisotropy); + } + } + + void SceneManager::clearCache() + { + // clear textures that are no longer referenced + for (std::set >::const_iterator it = mTexturesWithFilterSetting.begin(); it != mTexturesWithFilterSetting.end();) + { + osg::Texture* tex = *it; + if (tex->referenceCount() <= 1) + mTexturesWithFilterSetting.erase(it++); + else + ++it; + } + + } + } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 67fa2ab43..df330af5b 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace Resource { @@ -23,6 +24,11 @@ namespace osgUtil class IncrementalCompileOperation; } +namespace osgViewer +{ + class Viewer; +} + namespace Resource { @@ -69,11 +75,30 @@ namespace Resource /// @param mask The node mask to apply to loaded particle system nodes. void setParticleSystemMask(unsigned int mask); + void setFilterSettings(const std::string &magfilter, const std::string &minfilter, + const std::string &mipmap, int maxAnisotropy, + osgViewer::Viewer *viewer); + + /// Apply filter settings to the given texture. Note, when loading an object through this scene manager (i.e. calling getTemplate / createInstance) + /// the filter settings are applied automatically. This method is provided for textures that were created outside of the SceneManager. + void applyFilterSettings (osg::Texture* tex); + + /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts, + /// otherwise should be disabled to reduce memory usage. + void setUnRefImageDataAfterApply(bool unref); + + void clearCache(); + private: const VFS::Manager* mVFS; Resource::TextureManager* mTextureManager; Resource::NifFileManager* mNifFileManager; + osg::Texture::FilterMode mMinFilter; + osg::Texture::FilterMode mMagFilter; + int mMaxAnisotropy; + bool mUnRefImageDataAfterApply; + osg::ref_ptr mIncrementalCompileOperation; unsigned int mParticleSystemMask; @@ -82,8 +107,14 @@ namespace Resource typedef std::map > Index; Index mIndex; + std::set > mTexturesWithFilterSetting; + SceneManager(const SceneManager&); void operator = (const SceneManager&); + + /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! + void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); + }; } diff --git a/components/resource/texturemanager.cpp b/components/resource/texturemanager.cpp index d7f3fc61a..b49c6bb09 100644 --- a/components/resource/texturemanager.cpp +++ b/components/resource/texturemanager.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -47,11 +46,7 @@ namespace Resource TextureManager::TextureManager(const VFS::Manager *vfs) : mVFS(vfs) - , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) - , mMagFilter(osg::Texture::LINEAR) - , mMaxAnisotropy(1) , mWarningTexture(createWarningTexture()) - , mUnRefImageDataAfterApply(false) { } @@ -61,88 +56,6 @@ namespace Resource } - void TextureManager::setUnRefImageDataAfterApply(bool unref) - { - mUnRefImageDataAfterApply = unref; - } - - void TextureManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter, - const std::string &mipmap, int maxAnisotropy, - osgViewer::Viewer *viewer) - { - osg::Texture::FilterMode min = osg::Texture::LINEAR; - osg::Texture::FilterMode mag = osg::Texture::LINEAR; - - if(magfilter == "nearest") - mag = osg::Texture::NEAREST; - else if(magfilter != "linear") - std::cerr<< "Invalid texture mag filter: "<stopThreading(); - setFilterSettings(min, mag, maxAnisotropy); - if(viewer) viewer->startThreading(); - } - - void TextureManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) - { - mMinFilter = minFilter; - mMagFilter = magFilter; - mMaxAnisotropy = std::max(1, maxAnisotropy); - - for (std::map >::iterator it = mTextures.begin(); it != mTextures.end(); ++it) - { - osg::ref_ptr tex = it->second; - - // Keep mip-mapping disabled if the texture creator explicitely requested no mipmapping. - osg::Texture::FilterMode oldMin = tex->getFilter(osg::Texture::MIN_FILTER); - if (oldMin == osg::Texture::LINEAR || oldMin == osg::Texture::NEAREST) - { - osg::Texture::FilterMode newMin = osg::Texture::LINEAR; - switch (mMinFilter) - { - case osg::Texture::LINEAR: - case osg::Texture::LINEAR_MIPMAP_LINEAR: - case osg::Texture::LINEAR_MIPMAP_NEAREST: - newMin = osg::Texture::LINEAR; - break; - case osg::Texture::NEAREST: - case osg::Texture::NEAREST_MIPMAP_LINEAR: - case osg::Texture::NEAREST_MIPMAP_NEAREST: - newMin = osg::Texture::NEAREST; - break; - } - tex->setFilter(osg::Texture::MIN_FILTER, newMin); - } - else - tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); - - tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); - tex->setMaxAnisotropy(static_cast(mMaxAnisotropy)); - } - } - /* osg::ref_ptr TextureManager::getImage(const std::string &filename) { @@ -295,11 +208,6 @@ namespace Resource texture->setImage(image); texture->setWrap(osg::Texture::WRAP_S, wrapS); texture->setWrap(osg::Texture::WRAP_T, wrapT); - texture->setFilter(osg::Texture::MIN_FILTER, mMinFilter); - texture->setFilter(osg::Texture::MAG_FILTER, mMagFilter); - texture->setMaxAnisotropy(mMaxAnisotropy); - - texture->setUnRefImageDataAfterApply(mUnRefImageDataAfterApply); mTextures.insert(std::make_pair(key, texture)); return texture; diff --git a/components/resource/texturemanager.hpp b/components/resource/texturemanager.hpp index e12dfa090..61a727b5e 100644 --- a/components/resource/texturemanager.hpp +++ b/components/resource/texturemanager.hpp @@ -28,14 +28,6 @@ namespace Resource TextureManager(const VFS::Manager* vfs); ~TextureManager(); - void setFilterSettings(const std::string &magfilter, const std::string &minfilter, - const std::string &mipmap, int maxAnisotropy, - osgViewer::Viewer *view); - - /// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts, - /// otherwise should be disabled to reduce memory usage. - void setUnRefImageDataAfterApply(bool unref); - /// Create or retrieve a Texture2D using the specified image filename, and wrap parameters. /// Returns the dummy texture if the given texture is not found. osg::ref_ptr getTexture2D(const std::string& filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT); @@ -50,10 +42,6 @@ namespace Resource private: const VFS::Manager* mVFS; - osg::Texture::FilterMode mMinFilter; - osg::Texture::FilterMode mMagFilter; - int mMaxAnisotropy; - typedef std::pair, std::string> MapKey; std::map > mImages; @@ -62,11 +50,6 @@ namespace Resource osg::ref_ptr mWarningTexture; - bool mUnRefImageDataAfterApply; - - /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! - void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); - TextureManager(const TextureManager&); void operator = (const TextureManager&); }; From e2ee1d5689e1803cd7f63b94dbcfb7ff70b07f13 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 18:58:22 +0100 Subject: [PATCH 02/66] Use separate textures for the MyGUI RenderManager This means we can more reliably set the filter parameters. I believe this commit creates a regression where non-DDS GUI textures would display upside down, which will be addressed by further refactoring in the next commits. --- components/myguiplatform/myguitexture.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 50ac5c1f3..a42568cf8 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -86,7 +86,9 @@ namespace osgMyGUI if (!mTextureManager) throw std::runtime_error("No texturemanager set"); - mTexture = mTextureManager->getTexture2D(fname, osg::Texture2D::CLAMP_TO_EDGE, osg::Texture2D::CLAMP_TO_EDGE); + mTexture = new osg::Texture2D(mTextureManager->getImage(fname)); + mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); // disable mip-maps mTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); From 6ac688c0e2e16e4eec74ed5ef2d6a1a05a367d66 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 19:28:52 +0100 Subject: [PATCH 03/66] Change the way that texture filtering setting changes are applied at runtime to not require keeping a reference to textures The references would be difficult to clean up because there may or may not be another reference to the texture in the osgDB::SharedStateManager. --- components/resource/scenemanager.cpp | 68 ++++++++-------------------- components/resource/scenemanager.hpp | 6 +-- 2 files changed, 21 insertions(+), 53 deletions(-) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 80e6f737d..df103f50c 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -129,21 +129,14 @@ namespace Resource tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); tex->setMaxAnisotropy(mMaxAnisotropy); - mTexturesProcessed.insert(tex); } } } - const std::set >& getTexturesProcessed() - { - return mTexturesProcessed; - } - private: osg::Texture::FilterMode mMinFilter; osg::Texture::FilterMode mMagFilter; int mMaxAnisotropy; - std::set > mTexturesProcessed; }; /// Set texture filtering settings on textures contained in StateSets. @@ -199,20 +192,12 @@ namespace Resource tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); tex->setMaxAnisotropy(mMaxAnisotropy); - mTexturesProcessed.insert(tex); } } - - const std::set >& getTexturesProcessed() - { - return mTexturesProcessed; - } - private: osg::Texture::FilterMode mMinFilter; osg::Texture::FilterMode mMagFilter; int mMaxAnisotropy; - std::set > mTexturesProcessed; }; @@ -341,10 +326,6 @@ namespace Resource SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy); loaded->accept(setFilterSettingsControllerVisitor); - // remember which textures we set a filtering setting on so we can re-apply it when the setting changes - mTexturesWithFilterSetting.insert(setFilterSettingsVisitor.getTexturesProcessed().begin(), setFilterSettingsVisitor.getTexturesProcessed().end()); - mTexturesWithFilterSetting.insert(setFilterSettingsControllerVisitor.getTexturesProcessed().begin(), setFilterSettingsControllerVisitor.getTexturesProcessed().end()); - // share state osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); @@ -447,7 +428,26 @@ namespace Resource } if(viewer) viewer->stopThreading(); - setFilterSettings(min, mag, maxAnisotropy); + + mMinFilter = min; + mMagFilter = mag; + mMaxAnisotropy = std::max(1, maxAnisotropy); + + SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor (mMinFilter, mMagFilter, mMaxAnisotropy); + SetFilterSettingsVisitor setFilterSettingsVisitor (mMinFilter, mMagFilter, mMaxAnisotropy); + if (viewer && viewer->getSceneData()) + { + viewer->getSceneData()->accept(setFilterSettingsControllerVisitor); + viewer->getSceneData()->accept(setFilterSettingsVisitor); + } + + for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it) + { + osg::Node* node = it->second; + node->accept(setFilterSettingsControllerVisitor); + node->accept(setFilterSettingsVisitor); + } + if(viewer) viewer->startThreading(); } @@ -456,7 +456,6 @@ namespace Resource tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); tex->setMaxAnisotropy(mMaxAnisotropy); - mTexturesWithFilterSetting.insert(tex); } void SceneManager::setUnRefImageDataAfterApply(bool unref) @@ -464,32 +463,5 @@ namespace Resource mUnRefImageDataAfterApply = unref; } - void SceneManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy) - { - mMinFilter = minFilter; - mMagFilter = magFilter; - mMaxAnisotropy = std::max(1, maxAnisotropy); - - for (std::set >::const_iterator it = mTexturesWithFilterSetting.begin(); it != mTexturesWithFilterSetting.end(); ++it) - { - (*it)->setFilter(osg::Texture::MIN_FILTER, mMinFilter); - (*it)->setFilter(osg::Texture::MAG_FILTER, mMagFilter); - (*it)->setMaxAnisotropy(mMaxAnisotropy); - } - } - - void SceneManager::clearCache() - { - // clear textures that are no longer referenced - for (std::set >::const_iterator it = mTexturesWithFilterSetting.begin(); it != mTexturesWithFilterSetting.end();) - { - osg::Texture* tex = *it; - if (tex->referenceCount() <= 1) - mTexturesWithFilterSetting.erase(it++); - else - ++it; - } - - } } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index df330af5b..ae5fa2db6 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -87,8 +87,6 @@ namespace Resource /// otherwise should be disabled to reduce memory usage. void setUnRefImageDataAfterApply(bool unref); - void clearCache(); - private: const VFS::Manager* mVFS; Resource::TextureManager* mTextureManager; @@ -104,11 +102,9 @@ namespace Resource unsigned int mParticleSystemMask; // observer_ptr? - typedef std::map > Index; + typedef std::map > Index; Index mIndex; - std::set > mTexturesWithFilterSetting; - SceneManager(const SceneManager&); void operator = (const SceneManager&); From e8662bea3133ba9dbb09b86c3abb1af39425e90d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 20:23:41 +0100 Subject: [PATCH 04/66] Change the way that image origin is converted to OpenGL's lower-left convention Flip the texture coordinates instead of flipping textures. This simplifies the TextureManager (no need to worry if the caller wants flipping or not), should make it easier to generalize & multithread it. --- apps/openmw/mwgui/inventorywindow.cpp | 6 +++--- apps/openmw/mwgui/loadingscreen.cpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 8 ++++---- apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwgui/videowidget.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 2 +- .../myguiplatform/myguirendermanager.cpp | 7 +++++++ components/myguiplatform/myguitexture.cpp | 2 ++ components/nif/data.cpp | 10 ++++++++-- components/resource/texturemanager.cpp | 18 +++--------------- components/resource/texturemanager.hpp | 6 ++++++ components/sdlutil/sdlcursormanager.cpp | 2 +- files/mygui/openmw_pointer.xml | 4 ++-- 14 files changed, 41 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index facb17d66..4599132e3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -87,7 +87,7 @@ namespace MWGui mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); mAvatarImage->setRenderItemTexture(mPreviewTexture.get()); - mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); @@ -403,8 +403,8 @@ namespace MWGui int height = std::min(mPreview->getTextureHeight(), size.height); mPreview->setViewport(width, height); - mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, height/float(mPreview->getTextureHeight()), - width/float(mPreview->getTextureWidth()), 0.f)); + mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, + width/float(mPreview->getTextureWidth()), height/float(mPreview->getTextureHeight()))); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index e32140cb3..6385eb2c6 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -164,7 +164,7 @@ namespace MWGui mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); - mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } setVisible(true); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 362d2d9e0..1b8d5ab43 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -254,7 +254,7 @@ namespace MWGui { boost::shared_ptr myguitex (new osgMyGUI::OSGTexture(tex)); fog->setRenderItemTexture(myguitex.get()); - fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); + fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); fogTextures.push_back(myguitex); } else @@ -391,7 +391,7 @@ namespace MWGui boost::shared_ptr guiTex (new osgMyGUI::OSGTexture(texture)); textures.push_back(guiTex); box->setRenderItemTexture(guiTex.get()); - box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } else box->setRenderItemTexture(NULL); @@ -770,11 +770,11 @@ namespace MWGui mGlobalMapTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getBaseTexture())); mGlobalMapImage->setRenderItemTexture(mGlobalMapTexture.get()); - mGlobalMapImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + mGlobalMapImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); mGlobalMapOverlayTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getOverlayTexture())); mGlobalMapOverlay->setRenderItemTexture(mGlobalMapOverlayTexture.get()); - mGlobalMapOverlay->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + mGlobalMapOverlay->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } MapWindow::~MapWindow() diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index a65379fca..aa229de72 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -142,7 +142,7 @@ namespace MWGui mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreviewImage->setRenderItemTexture(mPreviewTexture.get()); - mPreviewImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + mPreviewImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); const ESM::NPC& proto = mPreview->getPrototype(); setRaceId(proto.mRace); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 53c280737..9de2374ad 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -422,6 +422,6 @@ namespace MWGui mScreenshotTexture.reset(new osgMyGUI::OSGTexture(texture)); mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); - mScreenshot->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); + mScreenshot->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } } diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index d28ea0b66..5a4bb981f 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -50,7 +50,7 @@ void VideoWidget::playVideo(const std::string &video) mTexture.reset(new osgMyGUI::OSGTexture(texture)); setRenderItemTexture(mTexture.get()); - getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); + getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f)); } int VideoWidget::getVideoWidth() diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index a6f68b5d4..258854054 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -194,7 +194,7 @@ namespace MWRender sizeX = std::max(sizeX, 0); sizeY = std::max(sizeY, 0); - mCamera->setViewport(0, 0, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY)); + mCamera->setViewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY)); redraw(); } diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 5bd56dc8f..f5bebd560 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -189,6 +190,12 @@ public: mStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON); mStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + + // need to flip tex coords since MyGUI uses DirectX convention of top left image origin + osg::Matrix flipMat; + flipMat.preMultTranslate(osg::Vec3f(0,1,0)); + flipMat.preMultScale(osg::Vec3f(1,-1,1)); + mStateSet->setTextureAttribute(0, new osg::TexMat(flipMat), osg::StateAttribute::ON); } Drawable(const Drawable ©, const osg::CopyOp ©op=osg::CopyOp::SHALLOW_COPY) : osg::Drawable(copy, copyop) diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index a42568cf8..3560a3ae3 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -145,6 +145,8 @@ namespace osgMyGUI if (!mLockedImage.valid()) throw std::runtime_error("Texture not locked"); + mLockedImage->flipVertical(); + // mTexture might be in use by the draw thread, so create a new texture instead and use that. osg::ref_ptr newTexture = new osg::Texture2D; newTexture->setTextureSize(getWidth(), getHeight()); diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 5a60ab8a5..08c268dde 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -65,8 +65,14 @@ void ShapeData::read(NIFStream *nif) uvlist.resize(uvs); for(int i = 0;i < uvs;i++) { - uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX); - nif->getVector2s(uvlist[i], verts); + osg::Vec2Array* list = uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX); + nif->getVector2s(list, verts); + + // flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin + for (unsigned int uv=0; uvsize(); ++uv) + { + (*list)[uv] = osg::Vec2((*list)[uv].x(), 1.f - (*list)[uv].y()); + } } } } diff --git a/components/resource/texturemanager.cpp b/components/resource/texturemanager.cpp index b49c6bb09..709b925f1 100644 --- a/components/resource/texturemanager.cpp +++ b/components/resource/texturemanager.cpp @@ -47,8 +47,8 @@ namespace Resource TextureManager::TextureManager(const VFS::Manager *vfs) : mVFS(vfs) , mWarningTexture(createWarningTexture()) + , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) { - } TextureManager::~TextureManager() @@ -116,8 +116,6 @@ namespace Resource return NULL; } - osg::ref_ptr opts (new osgDB::Options); - opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option size_t extPos = normalized.find_last_of('.'); std::string ext; if (extPos != std::string::npos && extPos+1 < normalized.size()) @@ -129,7 +127,7 @@ namespace Resource return NULL; } - osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts); + osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); if (!result.success()) { std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; @@ -170,8 +168,6 @@ namespace Resource return mWarningTexture; } - osg::ref_ptr opts (new osgDB::Options); - opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option size_t extPos = normalized.find_last_of('.'); std::string ext; if (extPos != std::string::npos && extPos+1 < normalized.size()) @@ -183,7 +179,7 @@ namespace Resource return mWarningTexture; } - osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts); + osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); if (!result.success()) { std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; @@ -196,14 +192,6 @@ namespace Resource return mWarningTexture; } - // We need to flip images, because the Morrowind texture coordinates use the DirectX convention (top-left image origin), - // but OpenGL uses bottom left as the image origin. - // For some reason this doesn't concern DDS textures, which are already flipped when loaded. - if (ext != "dds") - { - image->flipVertical(); - } - osg::ref_ptr texture(new osg::Texture2D); texture->setImage(image); texture->setWrap(osg::Texture::WRAP_S, wrapS); diff --git a/components/resource/texturemanager.hpp b/components/resource/texturemanager.hpp index 61a727b5e..cb2711aa7 100644 --- a/components/resource/texturemanager.hpp +++ b/components/resource/texturemanager.hpp @@ -18,6 +18,11 @@ namespace VFS class Manager; } +namespace osgDB +{ + class Options; +} + namespace Resource { @@ -49,6 +54,7 @@ namespace Resource std::map > mTextures; osg::ref_ptr mWarningTexture; + osg::ref_ptr mOptions; TextureManager(const TextureManager&); void operator = (const TextureManager&); diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 9ecef0483..afe240609 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -231,7 +231,7 @@ namespace SDLUtil return; } - SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, false); + SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, true); //set the cursor and store it for later SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y); diff --git a/files/mygui/openmw_pointer.xml b/files/mygui/openmw_pointer.xml index a55a5453c..4a2f81860 100644 --- a/files/mygui/openmw_pointer.xml +++ b/files/mygui/openmw_pointer.xml @@ -23,13 +23,13 @@ - + - + From 31988ca4cc643e9b1e76597f8574a30216513cc9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 21:07:08 +0100 Subject: [PATCH 05/66] Add a dont_override_filter description for textures that should be left alone --- components/resource/scenemanager.cpp | 7 +++++++ components/terrain/terraingrid.cpp | 1 + 2 files changed, 8 insertions(+) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index df103f50c..065187c79 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -189,6 +189,13 @@ namespace Resource osg::Texture* tex = attr->asTexture(); if (tex) { + if (tex->getUserDataContainer()) + { + const std::vector& descriptions = tex->getUserDataContainer()->getDescriptions(); + if (std::find(descriptions.begin(), descriptions.end(), "dont_override_filter") != descriptions.end()) + return; + } + tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter); tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter); tex->setMaxAnisotropy(mMaxAnisotropy); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index fb037c003..b7d727730 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -151,6 +151,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setResizeNonPowerOfTwoHint(false); + texture->getOrCreateUserDataContainer()->addDescription("dont_override_filter"); blendmapTextures.push_back(texture); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back()); From 71401aafe7eaef6b864fb0b1e6ad86f89e9a70c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 21:08:32 +0100 Subject: [PATCH 06/66] Handle multipass techniques in SetFilterSettingsVisitor --- components/resource/scenemanager.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 065187c79..e3f38e886 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -6,6 +6,7 @@ #include #include +#include #include @@ -153,12 +154,29 @@ namespace Resource virtual void apply(osg::Node& node) { + if (osgFX::Effect* effect = dynamic_cast(&node)) + apply(*effect); + osg::StateSet* stateset = node.getStateSet(); if (stateset) apply(stateset); + traverse(node); } + void apply(osgFX::Effect& effect) + { + for (int i =0; igetNumPasses(); ++pass) + { + if (tech->getPassStateSet(pass)) + apply(tech->getPassStateSet(pass)); + } + } + } + virtual void apply(osg::Geode& geode) { osg::StateSet* stateset = geode.getStateSet(); From fbd4ad9b0cac68457a33834ee7482c08d95e3c01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 22:46:15 +0100 Subject: [PATCH 07/66] Flip terrain textures --- components/esmterrain/storage.cpp | 4 ++-- components/terrain/buffercache.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 4be56d5e6..267b831ec 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -399,9 +399,9 @@ namespace ESMTerrain int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; if (blendIndex == i) - pData[y*blendmapSize*channels + x*channels + channel] = 255; + pData[(blendmapSize - y - 1)*blendmapSize*channels + x*channels + channel] = 255; else - pData[y*blendmapSize*channels + x*channels + channel] = 0; + pData[(blendmapSize - y - 1)*blendmapSize*channels + x*channels + channel] = 0; } } diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index a64f8ffd1..f9c860d47 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -193,7 +193,7 @@ namespace Terrain for (unsigned int row = 0; row < mNumVerts; ++row) { uvs->push_back(osg::Vec2f(col / static_cast(mNumVerts-1), - row / static_cast(mNumVerts-1))); + ((mNumVerts-1) - row) / static_cast(mNumVerts-1))); } } From 6ef848b7c50497438dd0cf85a17709a7bb0dfb75 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 22:58:02 +0100 Subject: [PATCH 08/66] Remove TextureManager::getTexture2D Instead use getImage and let the caller create the Texture. Sharing of textures is then handled in post by the SharedStateManager. This is closer to what the OSG serializer does. Streamlines the TextureManager and will make it easier to multithread. --- apps/openmw/mwgui/windowmanagerimp.cpp | 6 +- apps/openmw/mwrender/animation.cpp | 5 +- apps/openmw/mwrender/renderingmanager.cpp | 7 +++ apps/openmw/mwrender/renderingmanager.hpp | 2 + apps/openmw/mwrender/ripplesimulation.cpp | 7 ++- apps/openmw/mwrender/sky.cpp | 42 ++++++++----- apps/openmw/mwrender/util.cpp | 6 +- apps/openmw/mwrender/water.cpp | 8 ++- apps/openmw/mwworld/scene.cpp | 4 +- components/nifosg/nifloader.cpp | 11 ++-- components/resource/texturemanager.cpp | 75 ++--------------------- components/resource/texturemanager.hpp | 13 ++-- components/terrain/terraingrid.cpp | 30 ++++++++- components/terrain/terraingrid.hpp | 6 ++ components/terrain/world.hpp | 2 + 15 files changed, 114 insertions(+), 110 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 90c0494f6..346c672f7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2015,9 +2015,9 @@ namespace MWGui continue; std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture; - osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(tex_name, osg::Texture::CLAMP, osg::Texture::CLAMP); + osg::ref_ptr image = mResourceSystem->getTextureManager()->getImage(tex_name); - if(tex.valid()) + if(image.valid()) { //everything looks good, send it to the cursor manager Uint8 size_x = imgSetPointer->getSize().width; @@ -2026,7 +2026,7 @@ namespace MWGui Uint8 hotspot_y = imgSetPointer->getHotSpot().top; int rotation = imgSetPointer->getRotation(); - mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, tex->getImage(), size_x, size_y, hotspot_x, hotspot_y); + mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, image, size_x, size_y, hotspot_x, hotspot_y); } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a7bb3f128..35602fa17 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1059,7 +1059,10 @@ namespace MWRender stream << i; stream << ".dds"; - osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT); + osg::ref_ptr image = mResourceSystem->getTextureManager()->getImage(stream.str()); + osg::ref_ptr tex (new osg::Texture2D(image)); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT); mResourceSystem->getSceneManager()->applyFilterSettings(tex); textures.push_back(tex); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 755a8019b..2237d8125 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -231,6 +231,13 @@ namespace MWRender return mResourceSystem; } + void RenderingManager::clearCache() + { + mResourceSystem->clearCache(); + if (mTerrain.get()) + mTerrain->clearCache(); + } + osg::Group* RenderingManager::getLightRoot() { return mLightRoot.get(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 3b583af89..550e48e63 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -65,6 +65,8 @@ namespace MWRender Resource::ResourceSystem* getResourceSystem(); + void clearCache(); + osg::Group* getLightRoot(); void setNightEyeFactor(float factor); diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index f74631c4a..603cd397b 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "vismask.hpp" @@ -39,7 +40,11 @@ namespace { std::ostringstream texname; texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds"; - textures.push_back(resourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); + osg::ref_ptr tex (new osg::Texture2D(resourceSystem->getTextureManager()->getImage(texname.str()))); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + resourceSystem->getSceneManager()->applyFilterSettings(tex); + textures.push_back(tex); } osg::ref_ptr controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures)); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 513fc74a3..2a25baea9 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -476,9 +476,9 @@ public: mTransform->addUpdateCallback(mUpdater); mTransform->setNodeMask(Mask_Sun); - osg::ref_ptr sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds", - osg::Texture::CLAMP, - osg::Texture::CLAMP); + osg::ref_ptr sunTex (new osg::Texture2D(textureManager.getImage("textures/tx_sun_05.dds"))); + sunTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + sunTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); @@ -604,9 +604,9 @@ private: void createSunFlash(Resource::TextureManager& textureManager) { - osg::ref_ptr tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds", - osg::Texture::CLAMP, - osg::Texture::CLAMP); + osg::ref_ptr tex (new osg::Texture2D(textureManager.getImage("textures/tx_sun_flash_grey_05.dds"))); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); osg::ref_ptr transform (new osg::PositionAttitudeTransform); const float scale = 2.6f; @@ -1055,8 +1055,12 @@ private: void setTextures(const std::string& phaseTex, const std::string& circleTex) { - mPhaseTex = mTextureManager.getTexture2D(phaseTex, osg::Texture::CLAMP, osg::Texture::CLAMP); - mCircleTex = mTextureManager.getTexture2D(circleTex, osg::Texture::CLAMP, osg::Texture::CLAMP); + mPhaseTex = new osg::Texture2D(mTextureManager.getImage(phaseTex)); + mPhaseTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mPhaseTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mCircleTex = new osg::Texture2D(mTextureManager.getImage(circleTex)); + mCircleTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mCircleTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); reset(); } @@ -1342,8 +1346,12 @@ void SkyManager::createRain() mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1)); osg::ref_ptr stateset (mRainParticleSystem->getOrCreateStateSet()); - stateset->setTextureAttributeAndModes(0, mSceneManager->getTextureManager()->getTexture2D("textures/tx_raindrop_01.dds", - osg::Texture::CLAMP, osg::Texture::CLAMP), osg::StateAttribute::ON); + + osg::ref_ptr raindropTex (new osg::Texture2D(mSceneManager->getTextureManager()->getImage("textures/tx_raindrop_01.dds"))); + raindropTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + raindropTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + + stateset->setTextureAttributeAndModes(0, raindropTex, osg::StateAttribute::ON); stateset->setNestRenderBins(false); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); @@ -1549,8 +1557,11 @@ void SkyManager::setWeather(const WeatherResult& weather) std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); - mCloudUpdater->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, - osg::Texture::REPEAT, osg::Texture::REPEAT)); + osg::ref_ptr cloudTex (new osg::Texture2D(mSceneManager->getTextureManager()->getImage(texture))); + cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + + mCloudUpdater->setTexture(cloudTex); } if (mNextClouds != weather.mNextCloudTexture) @@ -1561,8 +1572,11 @@ void SkyManager::setWeather(const WeatherResult& weather) { std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); - mCloudUpdater2->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, - osg::Texture::REPEAT, osg::Texture::REPEAT)); + osg::ref_ptr cloudTex (new osg::Texture2D(mSceneManager->getTextureManager()->getImage(texture))); + cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + + mCloudUpdater2->setTexture(cloudTex); } } diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index e1af1c339..df6a0497f 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -15,8 +15,10 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou return; std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS()); // Not sure if wrap settings should be pulled from the overridden texture? - osg::ref_ptr tex = resourceSystem->getTextureManager()->getTexture2D(correctedTexture, osg::Texture2D::CLAMP, - osg::Texture2D::CLAMP); + osg::ref_ptr tex = new osg::Texture2D(resourceSystem->getTextureManager()->getImage(correctedTexture)); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + osg::ref_ptr stateset; if (node->getStateSet()) stateset = static_cast(node->getStateSet()->clone(osg::CopyOp::SHALLOW_COPY)); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index c7f2c9cc6..ff3156411 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -317,6 +317,7 @@ public: mRefractionTexture->setInternalFormat(GL_RGB); mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + mRefractionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter"); attach(osg::Camera::COLOR_BUFFER, mRefractionTexture); @@ -328,6 +329,7 @@ public: mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT); mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + mRefractionDepthTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter"); attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture); } @@ -388,6 +390,7 @@ public: mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mReflectionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter"); attach(osg::Camera::COLOR_BUFFER, mReflectionTexture); @@ -553,7 +556,10 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { std::ostringstream texname; texname << "textures/water/" << texture << std::setw(2) << std::setfill('0') << i << ".dds"; - textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); + osg::ref_ptr tex (new osg::Texture2D(mResourceSystem->getTextureManager()->getImage(texname.str()))); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + textures.push_back(tex); } if (!textures.size()) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 618a219f3..f30e6efe1 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -402,7 +402,7 @@ namespace MWWorld mCellChanged = true; - mRendering.getResourceSystem()->clearCache(); + mRendering.clearCache(); } void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) @@ -515,7 +515,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); - mRendering.getResourceSystem()->clearCache(); + mRendering.clearCache(); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 79f837953..190fd020f 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -709,7 +709,9 @@ namespace NifOsg } std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS()); - osg::ref_ptr texture = textureManager->getTexture2D(filename, wrapS, wrapT); + osg::ref_ptr texture (new osg::Texture2D(textureManager->getImage(filename))); + texture->setWrap(osg::Texture::WRAP_S, wrapS); + texture->setWrap(osg::Texture::WRAP_T, wrapT); textures.push_back(texture); } osg::ref_ptr callback(new FlipController(flipctrl, textures)); @@ -1376,9 +1378,10 @@ namespace NifOsg int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; - osg::ref_ptr texture2d = textureManager->getTexture2D(filename, - wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP, - wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); + // create a new texture, will later attempt to share using the SharedStateManager + osg::ref_ptr texture2d (new osg::Texture2D(textureManager->getImage(filename))); + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); int texUnit = boundTextures.size(); diff --git a/components/resource/texturemanager.cpp b/components/resource/texturemanager.cpp index 709b925f1..b3f23ab40 100644 --- a/components/resource/texturemanager.cpp +++ b/components/resource/texturemanager.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #ifdef OSG_LIBRARY_STATIC @@ -47,6 +45,7 @@ namespace Resource TextureManager::TextureManager(const VFS::Manager *vfs) : mVFS(vfs) , mWarningTexture(createWarningTexture()) + , mWarningImage(mWarningTexture->getImage()) , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) { } @@ -56,13 +55,6 @@ namespace Resource } - /* - osg::ref_ptr TextureManager::getImage(const std::string &filename) - { - - } - */ - bool checkSupported(osg::Image* image, const std::string& filename) { switch(image->getPixelFormat()) @@ -113,7 +105,7 @@ namespace Resource catch (std::exception& e) { std::cerr << "Failed to open image: " << e.what() << std::endl; - return NULL; + return mWarningImage; } size_t extPos = normalized.find_last_of('.'); @@ -124,20 +116,20 @@ namespace Resource if (!reader) { std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; - return NULL; + return mWarningImage; } osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); if (!result.success()) { std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; - return NULL; + return mWarningImage; } osg::Image* image = result.getImage(); if (!checkSupported(image, filename)) { - return NULL; + return mWarningImage; } mImages.insert(std::make_pair(normalized, image)); @@ -145,63 +137,6 @@ namespace Resource } } - osg::ref_ptr TextureManager::getTexture2D(const std::string &filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT) - { - std::string normalized = filename; - mVFS->normalizeFilename(normalized); - MapKey key = std::make_pair(std::make_pair(wrapS, wrapT), normalized); - std::map >::iterator found = mTextures.find(key); - if (found != mTextures.end()) - { - return found->second; - } - else - { - Files::IStreamPtr stream; - try - { - stream = mVFS->get(normalized.c_str()); - } - catch (std::exception& e) - { - std::cerr << "Failed to open texture: " << e.what() << std::endl; - return mWarningTexture; - } - - size_t extPos = normalized.find_last_of('.'); - std::string ext; - if (extPos != std::string::npos && extPos+1 < normalized.size()) - ext = normalized.substr(extPos+1); - osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); - if (!reader) - { - std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; - return mWarningTexture; - } - - osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); - if (!result.success()) - { - std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; - return mWarningTexture; - } - - osg::Image* image = result.getImage(); - if (!checkSupported(image, filename)) - { - return mWarningTexture; - } - - osg::ref_ptr texture(new osg::Texture2D); - texture->setImage(image); - texture->setWrap(osg::Texture::WRAP_S, wrapS); - texture->setWrap(osg::Texture::WRAP_T, wrapT); - - mTextures.insert(std::make_pair(key, texture)); - return texture; - } - } - osg::Texture2D* TextureManager::getWarningTexture() { return mWarningTexture.get(); diff --git a/components/resource/texturemanager.hpp b/components/resource/texturemanager.hpp index cb2711aa7..0290d2340 100644 --- a/components/resource/texturemanager.hpp +++ b/components/resource/texturemanager.hpp @@ -26,18 +26,15 @@ namespace osgDB namespace Resource { - /// @brief Handles loading/caching of Images and Texture StateAttributes. + /// @brief Handles loading/caching of Images. class TextureManager { public: TextureManager(const VFS::Manager* vfs); ~TextureManager(); - /// Create or retrieve a Texture2D using the specified image filename, and wrap parameters. - /// Returns the dummy texture if the given texture is not found. - osg::ref_ptr getTexture2D(const std::string& filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT); - /// Create or retrieve an Image + /// 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; } @@ -47,13 +44,11 @@ namespace Resource private: const VFS::Manager* mVFS; - typedef std::pair, std::string> MapKey; - + // TODO: use ObjectCache std::map > mImages; - std::map > mTextures; - osg::ref_ptr mWarningTexture; + osg::ref_ptr mWarningImage; osg::ref_ptr mOptions; TextureManager(const TextureManager&); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index b7d727730..58675a714 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -134,10 +135,19 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu // For compiling textures, I don't think the osgFX::Effect does it correctly osg::ref_ptr textureCompileDummy (new osg::Node); unsigned int dummyTextureCounter = 0; + std::vector > layerTextures; for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { - layerTextures.push_back(mResourceSystem->getTextureManager()->getTexture2D(it->mDiffuseMap, osg::Texture::REPEAT, osg::Texture::REPEAT)); + osg::ref_ptr tex = mTextureCache[it->mDiffuseMap]; + if (!tex) + { + tex = new osg::Texture2D(mResourceSystem->getTextureManager()->getImage(it->mDiffuseMap)); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + mTextureCache[it->mDiffuseMap] = tex; + } + layerTextures.push_back(tex); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back()); } @@ -148,15 +158,18 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu texture->setImage(*it); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); - texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setResizeNonPowerOfTwoHint(false); texture->getOrCreateUserDataContainer()->addDescription("dont_override_filter"); blendmapTextures.push_back(texture); + mResourceSystem->getSceneManager()->applyFilterSettings(texture); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back()); } + // SharedStatemanager->share(textureCompileDummy); + + // Remove unrefImageDataAfterApply for better state sharing + // use texture coordinates for both texture units, the layer texture and blend texture for (unsigned int i=0; i<2; ++i) geometry->setTexCoordArray(i, mCache.getUVBuffer()); @@ -226,4 +239,15 @@ void TerrainGrid::unloadCell(int x, int y) mGrid.erase(it); } +void TerrainGrid::clearCache() +{ + for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) + { + if (it->second->referenceCount() <= 1) + mTextureCache.erase(it++); + else + ++it; + } +} + } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 169a9a622..81bfc4211 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -27,12 +27,18 @@ namespace Terrain virtual void loadCell(int x, int y); virtual void unloadCell(int x, int y); + /// Clear cached textures that are no longer referenced + void clearCache(); + private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks unsigned int mNumSplits; + typedef std::map > TextureCache; + TextureCache mTextureCache; + typedef std::map, GridElement*> Grid; Grid mGrid; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4212f2a0c..bd5d0a466 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -39,6 +39,8 @@ namespace Terrain Storage* storage, int nodeMask); virtual ~World(); + virtual void clearCache() {} + float getHeightAt (const osg::Vec3f& worldPos); // This is only a hint and may be ignored by the implementation. From f99f403dda1160ed25172571b1dc22aa48f6b8f3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:03:53 +0100 Subject: [PATCH 09/66] Rename TextureManager to ImageManager --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/ripplesimulation.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 14 +++++++------- apps/openmw/mwrender/util.cpp | 2 +- apps/openmw/mwrender/water.cpp | 2 +- components/CMakeLists.txt | 2 +- components/myguiplatform/myguiplatform.cpp | 2 +- components/myguiplatform/myguiplatform.hpp | 4 ++-- components/myguiplatform/myguirendermanager.cpp | 4 ++-- components/myguiplatform/myguirendermanager.hpp | 6 +++--- components/myguiplatform/myguitexture.cpp | 4 ++-- components/myguiplatform/myguitexture.hpp | 6 +++--- components/nifosg/nifloader.cpp | 14 +++++++------- components/nifosg/nifloader.hpp | 4 ++-- .../{texturemanager.cpp => imagemanager.cpp} | 10 +++++----- .../{texturemanager.hpp => imagemanager.hpp} | 10 +++++----- components/resource/resourcesystem.cpp | 6 +++--- components/resource/resourcesystem.hpp | 6 +++--- components/resource/scenemanager.cpp | 12 ++++++------ components/resource/scenemanager.hpp | 8 ++++---- components/terrain/terraingrid.cpp | 2 +- 23 files changed, 63 insertions(+), 63 deletions(-) rename components/resource/{texturemanager.cpp => imagemanager.cpp} (94%) rename components/resource/{texturemanager.hpp => imagemanager.hpp} (83%) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 346c672f7..d053d8bfd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 35602fa17..2c5831dae 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include // KeyframeHolder #include diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2237d8125..d1b5f6fda 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 603cd397b..6cfcbc498 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 2a25baea9..0f70e2c02 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include @@ -134,7 +134,7 @@ private: class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater { public: - AtmosphereNightUpdater(Resource::TextureManager* textureManager) + AtmosphereNightUpdater(Resource::ImageManager* textureManager) { // we just need a texture, its contents don't really matter mTexture = textureManager->getWarningTexture(); @@ -469,7 +469,7 @@ const float CelestialBody::mDistance = 1000.0f; class Sun : public CelestialBody { public: - Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) + Sun(osg::Group* parentNode, Resource::ImageManager& textureManager) : CelestialBody(parentNode, 1.0f, 1) , mUpdater(new Updater) { @@ -602,7 +602,7 @@ private: return oqn; } - void createSunFlash(Resource::TextureManager& textureManager) + void createSunFlash(Resource::ImageManager& textureManager) { osg::ref_ptr tex (new osg::Texture2D(textureManager.getImage("textures/tx_sun_flash_grey_05.dds"))); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); @@ -932,7 +932,7 @@ public: Type_Secunda }; - Moon(osg::Group* parentNode, Resource::TextureManager& textureManager, float scaleFactor, Type type) + Moon(osg::Group* parentNode, Resource::ImageManager& textureManager, float scaleFactor, Type type) : CelestialBody(parentNode, scaleFactor, 2) , mType(type) , mPhase(MoonState::Phase_Unspecified) @@ -1001,7 +1001,7 @@ public: private: struct Updater : public SceneUtil::StateSetUpdater { - Resource::TextureManager& mTextureManager; + Resource::ImageManager& mTextureManager; osg::ref_ptr mPhaseTex; osg::ref_ptr mCircleTex; float mTransparency; @@ -1009,7 +1009,7 @@ private: osg::Vec4f mAtmosphereColor; osg::Vec4f mMoonColor; - Updater(Resource::TextureManager& textureManager) + Updater(Resource::ImageManager& textureManager) : mTextureManager(textureManager) , mPhaseTex() , mCircleTex() diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index df6a0497f..3c8d1408a 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include namespace MWRender diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index ff3156411..7d1221a53 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 089779eda..0b54ddeb7 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -41,7 +41,7 @@ add_component_dir (vfs ) add_component_dir (resource - scenemanager keyframemanager texturemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache + scenemanager keyframemanager imagemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache ) add_component_dir (sceneutil diff --git a/components/myguiplatform/myguiplatform.cpp b/components/myguiplatform/myguiplatform.cpp index 22b88438f..491af8486 100644 --- a/components/myguiplatform/myguiplatform.cpp +++ b/components/myguiplatform/myguiplatform.cpp @@ -7,7 +7,7 @@ namespace osgMyGUI { -Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::TextureManager *textureManager, float uiScalingFactor) +Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::ImageManager *textureManager, float uiScalingFactor) : mRenderManager(nullptr) , mDataManager(nullptr) , mLogManager(nullptr) diff --git a/components/myguiplatform/myguiplatform.hpp b/components/myguiplatform/myguiplatform.hpp index 90d45ce20..b3f959798 100644 --- a/components/myguiplatform/myguiplatform.hpp +++ b/components/myguiplatform/myguiplatform.hpp @@ -13,7 +13,7 @@ namespace osg } namespace Resource { - class TextureManager; + class ImageManager; } namespace MyGUI { @@ -30,7 +30,7 @@ namespace osgMyGUI class Platform { public: - Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::TextureManager* textureManager, float uiScalingFactor); + Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* textureManager, float uiScalingFactor); ~Platform(); diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index f5bebd560..23d004f47 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -15,7 +15,7 @@ #include -#include +#include #include "myguitexture.hpp" @@ -353,7 +353,7 @@ void OSGVertexBuffer::create() // --------------------------------------------------------------------------- -RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::TextureManager* textureManager, float scalingFactor) +RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* textureManager, float scalingFactor) : mViewer(viewer) , mSceneRoot(sceneroot) , mTextureManager(textureManager) diff --git a/components/myguiplatform/myguirendermanager.hpp b/components/myguiplatform/myguirendermanager.hpp index f2251cdb0..b1aa0d3f0 100644 --- a/components/myguiplatform/myguirendermanager.hpp +++ b/components/myguiplatform/myguirendermanager.hpp @@ -7,7 +7,7 @@ namespace Resource { - class TextureManager; + class ImageManager; } namespace osgViewer @@ -33,7 +33,7 @@ class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget osg::ref_ptr mViewer; osg::ref_ptr mSceneRoot; osg::ref_ptr mDrawable; - Resource::TextureManager* mTextureManager; + Resource::ImageManager* mTextureManager; MyGUI::IntSize mViewSize; bool mUpdate; @@ -54,7 +54,7 @@ class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget void destroyAllResources(); public: - RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::TextureManager* textureManager, float scalingFactor); + RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* textureManager, float scalingFactor); virtual ~RenderManager(); void initialise(); diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 3560a3ae3..904ed6abf 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -5,12 +5,12 @@ #include -#include +#include namespace osgMyGUI { - OSGTexture::OSGTexture(const std::string &name, Resource::TextureManager* textureManager) + OSGTexture::OSGTexture(const std::string &name, Resource::ImageManager* textureManager) : mName(name) , mTextureManager(textureManager) , mFormat(MyGUI::PixelFormat::Unknow) diff --git a/components/myguiplatform/myguitexture.hpp b/components/myguiplatform/myguitexture.hpp index de385e94d..21bc89992 100644 --- a/components/myguiplatform/myguitexture.hpp +++ b/components/myguiplatform/myguitexture.hpp @@ -13,7 +13,7 @@ namespace osg namespace Resource { - class TextureManager; + class ImageManager; } namespace osgMyGUI @@ -21,7 +21,7 @@ namespace osgMyGUI class OSGTexture : public MyGUI::ITexture { std::string mName; - Resource::TextureManager* mTextureManager; + Resource::ImageManager* mTextureManager; osg::ref_ptr mLockedImage; osg::ref_ptr mTexture; @@ -30,7 +30,7 @@ namespace osgMyGUI size_t mNumElemBytes; public: - OSGTexture(const std::string &name, Resource::TextureManager* textureManager); + OSGTexture(const std::string &name, Resource::ImageManager* textureManager); OSGTexture(osg::Texture2D* texture); virtual ~OSGTexture(); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 190fd020f..d09c34aa5 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -11,7 +11,7 @@ // resource #include #include -#include +#include // skel #include @@ -343,7 +343,7 @@ namespace NifOsg } } - osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::TextureManager* textureManager) + osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::ImageManager* textureManager) { if (nif->numRoots() < 1) nif->fail("Found no root nodes"); @@ -369,7 +369,7 @@ namespace NifOsg return created; } - void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, std::vector& boundTextures, int animflags) + void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* textureManager, std::vector& boundTextures, int animflags) { const Nif::PropertyList& props = nifNode->props; for (size_t i = 0; i handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::TextureManager* textureManager, + osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* textureManager, std::vector boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { osg::ref_ptr node = new osg::MatrixTransform(nifNode->trafo.toMatrix()); @@ -682,7 +682,7 @@ namespace NifOsg } } - void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, osg::StateSet *stateset, int animflags) + void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* textureManager, osg::StateSet *stateset, int animflags) { for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -1244,7 +1244,7 @@ namespace NifOsg } void handleProperty(const Nif::Property *property, - osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, std::vector& boundTextures, int animflags) + osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* textureManager, std::vector& boundTextures, int animflags) { switch (property->recType) { @@ -1543,7 +1543,7 @@ namespace NifOsg }; - osg::ref_ptr Loader::load(Nif::NIFFilePtr file, Resource::TextureManager* textureManager) + osg::ref_ptr Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* textureManager) { LoaderImpl impl(file->getFilename()); return impl.load(file, textureManager); diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index e15df5302..afc912ed3 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -15,7 +15,7 @@ namespace osg namespace Resource { - class TextureManager; + class ImageManager; } namespace NifOsg @@ -62,7 +62,7 @@ namespace NifOsg { public: /// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton if so. - static osg::ref_ptr load(Nif::NIFFilePtr file, Resource::TextureManager* textureManager); + static osg::ref_ptr load(Nif::NIFFilePtr file, Resource::ImageManager* textureManager); /// Load keyframe controllers from the given kf file. static void loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target); diff --git a/components/resource/texturemanager.cpp b/components/resource/imagemanager.cpp similarity index 94% rename from components/resource/texturemanager.cpp rename to components/resource/imagemanager.cpp index b3f23ab40..8b14e7237 100644 --- a/components/resource/texturemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -1,4 +1,4 @@ -#include "texturemanager.hpp" +#include "imagemanager.hpp" #include #include @@ -42,7 +42,7 @@ namespace namespace Resource { - TextureManager::TextureManager(const VFS::Manager *vfs) + ImageManager::ImageManager(const VFS::Manager *vfs) : mVFS(vfs) , mWarningTexture(createWarningTexture()) , mWarningImage(mWarningTexture->getImage()) @@ -50,7 +50,7 @@ namespace Resource { } - TextureManager::~TextureManager() + ImageManager::~ImageManager() { } @@ -88,7 +88,7 @@ namespace Resource return true; } - osg::ref_ptr TextureManager::getImage(const std::string &filename) + osg::ref_ptr ImageManager::getImage(const std::string &filename) { std::string normalized = filename; mVFS->normalizeFilename(normalized); @@ -137,7 +137,7 @@ namespace Resource } } - osg::Texture2D* TextureManager::getWarningTexture() + osg::Texture2D* ImageManager::getWarningTexture() { return mWarningTexture.get(); } diff --git a/components/resource/texturemanager.hpp b/components/resource/imagemanager.hpp similarity index 83% rename from components/resource/texturemanager.hpp rename to components/resource/imagemanager.hpp index 0290d2340..14d0b673e 100644 --- a/components/resource/texturemanager.hpp +++ b/components/resource/imagemanager.hpp @@ -27,11 +27,11 @@ namespace Resource { /// @brief Handles loading/caching of Images. - class TextureManager + class ImageManager { public: - TextureManager(const VFS::Manager* vfs); - ~TextureManager(); + ImageManager(const VFS::Manager* vfs); + ~ImageManager(); /// Create or retrieve an Image /// Returns the dummy image if the given image is not found. @@ -51,8 +51,8 @@ namespace Resource osg::ref_ptr mWarningImage; osg::ref_ptr mOptions; - TextureManager(const TextureManager&); - void operator = (const TextureManager&); + ImageManager(const ImageManager&); + void operator = (const ImageManager&); }; } diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 2ce8d22e6..89a4b2cb3 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -1,7 +1,7 @@ #include "resourcesystem.hpp" #include "scenemanager.hpp" -#include "texturemanager.hpp" +#include "imagemanager.hpp" #include "niffilemanager.hpp" #include "keyframemanager.hpp" @@ -13,7 +13,7 @@ namespace Resource { mNifFileManager.reset(new NifFileManager(vfs)); mKeyframeManager.reset(new KeyframeManager(vfs)); - mTextureManager.reset(new TextureManager(vfs)); + mTextureManager.reset(new ImageManager(vfs)); mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get())); } @@ -27,7 +27,7 @@ namespace Resource return mSceneManager.get(); } - TextureManager* ResourceSystem::getTextureManager() + ImageManager* ResourceSystem::getTextureManager() { return mTextureManager.get(); } diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 3e1a793ca..7c4d83b7d 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -12,7 +12,7 @@ namespace Resource { class SceneManager; - class TextureManager; + class ImageManager; class NifFileManager; class KeyframeManager; @@ -26,7 +26,7 @@ namespace Resource ~ResourceSystem(); SceneManager* getSceneManager(); - TextureManager* getTextureManager(); + ImageManager* getTextureManager(); NifFileManager* getNifFileManager(); KeyframeManager* getKeyframeManager(); @@ -37,7 +37,7 @@ namespace Resource private: std::auto_ptr mSceneManager; - std::auto_ptr mTextureManager; + std::auto_ptr mTextureManager; std::auto_ptr mNifFileManager; std::auto_ptr mKeyframeManager; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index e3f38e886..287186887 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -24,7 +24,7 @@ #include #include -#include "texturemanager.hpp" +#include "imagemanager.hpp" #include "niffilemanager.hpp" namespace @@ -227,7 +227,7 @@ namespace Resource - SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) + SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* textureManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) , mTextureManager(textureManager) , mNifFileManager(nifFileManager) @@ -249,7 +249,7 @@ namespace Resource class ImageReadCallback : public osgDB::ReadFileCallback { public: - ImageReadCallback(Resource::TextureManager* textureMgr) + ImageReadCallback(Resource::ImageManager* textureMgr) : mTextureManager(textureMgr) { } @@ -267,7 +267,7 @@ namespace Resource } private: - Resource::TextureManager* mTextureManager; + Resource::ImageManager* mTextureManager; }; std::string getFileExtension(const std::string& file) @@ -278,7 +278,7 @@ namespace Resource return std::string(); } - osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::TextureManager* textureMgr, Resource::NifFileManager* nifFileManager) + osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* textureMgr, Resource::NifFileManager* nifFileManager) { std::string ext = getFileExtension(normalizedFilename); if (ext == "nif") @@ -408,7 +408,7 @@ namespace Resource return mVFS; } - Resource::TextureManager* SceneManager::getTextureManager() + Resource::ImageManager* SceneManager::getTextureManager() { return mTextureManager; } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index ae5fa2db6..6eadf7b45 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -10,7 +10,7 @@ namespace Resource { - class TextureManager; + class ImageManager; class NifFileManager; } @@ -36,7 +36,7 @@ namespace Resource class SceneManager { public: - SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager); + SceneManager(const VFS::Manager* vfs, Resource::ImageManager* textureManager, Resource::NifFileManager* nifFileManager); ~SceneManager(); /// Get a read-only copy of this scene "template" @@ -70,7 +70,7 @@ namespace Resource const VFS::Manager* getVFS() const; - Resource::TextureManager* getTextureManager(); + Resource::ImageManager* getTextureManager(); /// @param mask The node mask to apply to loaded particle system nodes. void setParticleSystemMask(unsigned int mask); @@ -89,7 +89,7 @@ namespace Resource private: const VFS::Manager* mVFS; - Resource::TextureManager* mTextureManager; + Resource::ImageManager* mTextureManager; Resource::NifFileManager* mNifFileManager; osg::Texture::FilterMode mMinFilter; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 58675a714..094023f38 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include From 5ee3d1698fd10584b07213f74c0a2a97b0fb06a9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:05:37 +0100 Subject: [PATCH 10/66] Remove getWarningTexture in favor of getWarningImage --- apps/openmw/mwrender/sky.cpp | 2 +- components/resource/imagemanager.cpp | 14 +++++--------- components/resource/imagemanager.hpp | 3 +-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 0f70e2c02..69fbb2379 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -137,7 +137,7 @@ public: AtmosphereNightUpdater(Resource::ImageManager* textureManager) { // we just need a texture, its contents don't really matter - mTexture = textureManager->getWarningTexture(); + mTexture = new osg::Texture2D(textureManager->getWarningImage()); } void setFade(const float fade) diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index 8b14e7237..e1c0572c7 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -17,7 +17,7 @@ USE_OSGPLUGIN(jpeg) namespace { - osg::ref_ptr createWarningTexture() + osg::ref_ptr createWarningImage() { osg::ref_ptr warningImage = new osg::Image; @@ -31,10 +31,7 @@ namespace data[3*i+1] = (0); data[3*i+2] = (255); } - - osg::ref_ptr warningTexture = new osg::Texture2D; - warningTexture->setImage(warningImage); - return warningTexture; + return warningImage; } } @@ -44,8 +41,7 @@ namespace Resource ImageManager::ImageManager(const VFS::Manager *vfs) : mVFS(vfs) - , mWarningTexture(createWarningTexture()) - , mWarningImage(mWarningTexture->getImage()) + , mWarningImage(createWarningImage()) , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) { } @@ -137,9 +133,9 @@ namespace Resource } } - osg::Texture2D* ImageManager::getWarningTexture() + osg::Image *ImageManager::getWarningImage() { - return mWarningTexture.get(); + return mWarningImage; } } diff --git a/components/resource/imagemanager.hpp b/components/resource/imagemanager.hpp index 14d0b673e..ee0c0f2e2 100644 --- a/components/resource/imagemanager.hpp +++ b/components/resource/imagemanager.hpp @@ -39,7 +39,7 @@ namespace Resource const VFS::Manager* getVFS() { return mVFS; } - osg::Texture2D* getWarningTexture(); + osg::Image* getWarningImage(); private: const VFS::Manager* mVFS; @@ -47,7 +47,6 @@ namespace Resource // TODO: use ObjectCache std::map > mImages; - osg::ref_ptr mWarningTexture; osg::ref_ptr mWarningImage; osg::ref_ptr mOptions; From 9e53e12c70f38c6de9d2ea515a58746c413dd753 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:10:27 +0100 Subject: [PATCH 11/66] More renaming of TextureManager -> ImageManager --- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +- apps/openmw/mwrender/animation.cpp | 2 +- apps/openmw/mwrender/ripplesimulation.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 42 +++++++++---------- apps/openmw/mwrender/util.cpp | 2 +- apps/openmw/mwrender/water.cpp | 2 +- components/myguiplatform/myguiplatform.cpp | 4 +- components/myguiplatform/myguiplatform.hpp | 2 +- .../myguiplatform/myguirendermanager.cpp | 6 +-- .../myguiplatform/myguirendermanager.hpp | 4 +- components/myguiplatform/myguitexture.cpp | 12 +++--- components/myguiplatform/myguitexture.hpp | 4 +- components/nifosg/nifloader.cpp | 32 +++++++------- components/nifosg/nifloader.hpp | 2 +- components/resource/imagemanager.hpp | 4 +- components/resource/resourcesystem.cpp | 8 ++-- components/resource/resourcesystem.hpp | 4 +- components/resource/scenemanager.cpp | 26 ++++++------ components/resource/scenemanager.hpp | 6 +-- components/terrain/terraingrid.cpp | 2 +- 20 files changed, 85 insertions(+), 85 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d053d8bfd..03fbc9238 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -194,7 +194,7 @@ namespace MWGui , mVersionDescription(versionDescription) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getTextureManager(), uiScale); + mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale); mGuiPlatform->initialise(resourcePath, logpath); mGui = new MyGUI::Gui; @@ -2015,7 +2015,7 @@ namespace MWGui continue; std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture; - osg::ref_ptr image = mResourceSystem->getTextureManager()->getImage(tex_name); + osg::ref_ptr image = mResourceSystem->getImageManager()->getImage(tex_name); if(image.valid()) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2c5831dae..b7c0c0a36 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1059,7 +1059,7 @@ namespace MWRender stream << i; stream << ".dds"; - osg::ref_ptr image = mResourceSystem->getTextureManager()->getImage(stream.str()); + osg::ref_ptr image = mResourceSystem->getImageManager()->getImage(stream.str()); osg::ref_ptr tex (new osg::Texture2D(image)); tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT); tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT); diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 6cfcbc498..f43f1ee5c 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -40,7 +40,7 @@ namespace { std::ostringstream texname; texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds"; - osg::ref_ptr tex (new osg::Texture2D(resourceSystem->getTextureManager()->getImage(texname.str()))); + osg::ref_ptr tex (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str()))); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); resourceSystem->getSceneManager()->applyFilterSettings(tex); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 69fbb2379..0d3bf1a66 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -134,10 +134,10 @@ private: class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater { public: - AtmosphereNightUpdater(Resource::ImageManager* textureManager) + AtmosphereNightUpdater(Resource::ImageManager* imageManager) { // we just need a texture, its contents don't really matter - mTexture = new osg::Texture2D(textureManager->getWarningImage()); + mTexture = new osg::Texture2D(imageManager->getWarningImage()); } void setFade(const float fade) @@ -469,14 +469,14 @@ const float CelestialBody::mDistance = 1000.0f; class Sun : public CelestialBody { public: - Sun(osg::Group* parentNode, Resource::ImageManager& textureManager) + Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) : CelestialBody(parentNode, 1.0f, 1) , mUpdater(new Updater) { mTransform->addUpdateCallback(mUpdater); mTransform->setNodeMask(Mask_Sun); - osg::ref_ptr sunTex (new osg::Texture2D(textureManager.getImage("textures/tx_sun_05.dds"))); + osg::ref_ptr sunTex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_05.dds"))); sunTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); sunTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -498,7 +498,7 @@ public: mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); - createSunFlash(textureManager); + createSunFlash(imageManager); createSunGlare(); } @@ -602,9 +602,9 @@ private: return oqn; } - void createSunFlash(Resource::ImageManager& textureManager) + void createSunFlash(Resource::ImageManager& imageManager) { - osg::ref_ptr tex (new osg::Texture2D(textureManager.getImage("textures/tx_sun_flash_grey_05.dds"))); + osg::ref_ptr tex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_flash_grey_05.dds"))); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -932,11 +932,11 @@ public: Type_Secunda }; - Moon(osg::Group* parentNode, Resource::ImageManager& textureManager, float scaleFactor, Type type) + Moon(osg::Group* parentNode, Resource::ImageManager& imageManager, float scaleFactor, Type type) : CelestialBody(parentNode, scaleFactor, 2) , mType(type) , mPhase(MoonState::Phase_Unspecified) - , mUpdater(new Updater(textureManager)) + , mUpdater(new Updater(imageManager)) { setPhase(MoonState::Phase_Full); setVisible(true); @@ -1001,7 +1001,7 @@ public: private: struct Updater : public SceneUtil::StateSetUpdater { - Resource::ImageManager& mTextureManager; + Resource::ImageManager& mImageManager; osg::ref_ptr mPhaseTex; osg::ref_ptr mCircleTex; float mTransparency; @@ -1009,8 +1009,8 @@ private: osg::Vec4f mAtmosphereColor; osg::Vec4f mMoonColor; - Updater(Resource::ImageManager& textureManager) - : mTextureManager(textureManager) + Updater(Resource::ImageManager& imageManager) + : mImageManager(imageManager) , mPhaseTex() , mCircleTex() , mTransparency(1.0f) @@ -1055,10 +1055,10 @@ private: void setTextures(const std::string& phaseTex, const std::string& circleTex) { - mPhaseTex = new osg::Texture2D(mTextureManager.getImage(phaseTex)); + mPhaseTex = new osg::Texture2D(mImageManager.getImage(phaseTex)); mPhaseTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mPhaseTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - mCircleTex = new osg::Texture2D(mTextureManager.getImage(circleTex)); + mCircleTex = new osg::Texture2D(mImageManager.getImage(circleTex)); mCircleTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mCircleTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -1165,14 +1165,14 @@ void SkyManager::create() atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); ModVertexAlphaVisitor modStars(2); atmosphereNight->accept(modStars); - mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getTextureManager()); + mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager()); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); - mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getTextureManager())); + mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager())); const Fallback::Map* fallback=MWBase::Environment::get().getWorld()->getFallback(); - mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); - mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); + mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); + mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); mCloudNode = new osg::PositionAttitudeTransform; mEarlyRenderBinRoot->addChild(mCloudNode); @@ -1347,7 +1347,7 @@ void SkyManager::createRain() osg::ref_ptr stateset (mRainParticleSystem->getOrCreateStateSet()); - osg::ref_ptr raindropTex (new osg::Texture2D(mSceneManager->getTextureManager()->getImage("textures/tx_raindrop_01.dds"))); + osg::ref_ptr raindropTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage("textures/tx_raindrop_01.dds"))); raindropTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); raindropTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -1557,7 +1557,7 @@ void SkyManager::setWeather(const WeatherResult& weather) std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); - osg::ref_ptr cloudTex (new osg::Texture2D(mSceneManager->getTextureManager()->getImage(texture))); + osg::ref_ptr cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture))); cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); @@ -1572,7 +1572,7 @@ void SkyManager::setWeather(const WeatherResult& weather) { std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); - osg::ref_ptr cloudTex (new osg::Texture2D(mSceneManager->getTextureManager()->getImage(texture))); + osg::ref_ptr cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture))); cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index 3c8d1408a..0e1eaf0f4 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -15,7 +15,7 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou return; std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS()); // Not sure if wrap settings should be pulled from the overridden texture? - osg::ref_ptr tex = new osg::Texture2D(resourceSystem->getTextureManager()->getImage(correctedTexture)); + osg::ref_ptr tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture)); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 7d1221a53..0e4f1a974 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -556,7 +556,7 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { std::ostringstream texname; texname << "textures/water/" << texture << std::setw(2) << std::setfill('0') << i << ".dds"; - osg::ref_ptr tex (new osg::Texture2D(mResourceSystem->getTextureManager()->getImage(texname.str()))); + osg::ref_ptr tex (new osg::Texture2D(mResourceSystem->getImageManager()->getImage(texname.str()))); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); textures.push_back(tex); diff --git a/components/myguiplatform/myguiplatform.cpp b/components/myguiplatform/myguiplatform.cpp index 491af8486..dfb2e6539 100644 --- a/components/myguiplatform/myguiplatform.cpp +++ b/components/myguiplatform/myguiplatform.cpp @@ -7,14 +7,14 @@ namespace osgMyGUI { -Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::ImageManager *textureManager, float uiScalingFactor) +Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::ImageManager *imageManager, float uiScalingFactor) : mRenderManager(nullptr) , mDataManager(nullptr) , mLogManager(nullptr) , mLogFacility(nullptr) { mLogManager = new MyGUI::LogManager(); - mRenderManager = new RenderManager(viewer, guiRoot, textureManager, uiScalingFactor); + mRenderManager = new RenderManager(viewer, guiRoot, imageManager, uiScalingFactor); mDataManager = new DataManager(); } diff --git a/components/myguiplatform/myguiplatform.hpp b/components/myguiplatform/myguiplatform.hpp index b3f959798..5ffbe0be7 100644 --- a/components/myguiplatform/myguiplatform.hpp +++ b/components/myguiplatform/myguiplatform.hpp @@ -30,7 +30,7 @@ namespace osgMyGUI class Platform { public: - Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* textureManager, float uiScalingFactor); + Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* imageManager, float uiScalingFactor); ~Platform(); diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 23d004f47..5d2e3a9ae 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -353,10 +353,10 @@ void OSGVertexBuffer::create() // --------------------------------------------------------------------------- -RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* textureManager, float scalingFactor) +RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* imageManager, float scalingFactor) : mViewer(viewer) , mSceneRoot(sceneroot) - , mTextureManager(textureManager) + , mImageManager(imageManager) , mUpdate(false) , mIsInitialise(false) , mInvScalingFactor(1.f) @@ -523,7 +523,7 @@ MyGUI::ITexture* RenderManager::createTexture(const std::string &name) mTextures.erase(item); } - OSGTexture* texture = new OSGTexture(name, mTextureManager); + OSGTexture* texture = new OSGTexture(name, mImageManager); mTextures.insert(std::make_pair(name, texture)); return texture; } diff --git a/components/myguiplatform/myguirendermanager.hpp b/components/myguiplatform/myguirendermanager.hpp index b1aa0d3f0..4a0aae3cd 100644 --- a/components/myguiplatform/myguirendermanager.hpp +++ b/components/myguiplatform/myguirendermanager.hpp @@ -33,7 +33,7 @@ class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget osg::ref_ptr mViewer; osg::ref_ptr mSceneRoot; osg::ref_ptr mDrawable; - Resource::ImageManager* mTextureManager; + Resource::ImageManager* mImageManager; MyGUI::IntSize mViewSize; bool mUpdate; @@ -54,7 +54,7 @@ class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget void destroyAllResources(); public: - RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* textureManager, float scalingFactor); + RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* imageManager, float scalingFactor); virtual ~RenderManager(); void initialise(); diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 904ed6abf..2ba78c26b 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -10,9 +10,9 @@ namespace osgMyGUI { - OSGTexture::OSGTexture(const std::string &name, Resource::ImageManager* textureManager) + OSGTexture::OSGTexture(const std::string &name, Resource::ImageManager* imageManager) : mName(name) - , mTextureManager(textureManager) + , mImageManager(imageManager) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) , mNumElemBytes(0) @@ -20,7 +20,7 @@ namespace osgMyGUI } OSGTexture::OSGTexture(osg::Texture2D *texture) - : mTextureManager(NULL) + : mImageManager(NULL) , mTexture(texture) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) @@ -83,10 +83,10 @@ namespace osgMyGUI void OSGTexture::loadFromFile(const std::string &fname) { - if (!mTextureManager) - throw std::runtime_error("No texturemanager set"); + if (!mImageManager) + throw std::runtime_error("No imagemanager set"); - mTexture = new osg::Texture2D(mTextureManager->getImage(fname)); + mTexture = new osg::Texture2D(mImageManager->getImage(fname)); mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); // disable mip-maps diff --git a/components/myguiplatform/myguitexture.hpp b/components/myguiplatform/myguitexture.hpp index 21bc89992..101e2135b 100644 --- a/components/myguiplatform/myguitexture.hpp +++ b/components/myguiplatform/myguitexture.hpp @@ -21,7 +21,7 @@ namespace osgMyGUI class OSGTexture : public MyGUI::ITexture { std::string mName; - Resource::ImageManager* mTextureManager; + Resource::ImageManager* mImageManager; osg::ref_ptr mLockedImage; osg::ref_ptr mTexture; @@ -30,7 +30,7 @@ namespace osgMyGUI size_t mNumElemBytes; public: - OSGTexture(const std::string &name, Resource::ImageManager* textureManager); + OSGTexture(const std::string &name, Resource::ImageManager* imageManager); OSGTexture(osg::Texture2D* texture); virtual ~OSGTexture(); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d09c34aa5..6c6061063 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -343,7 +343,7 @@ namespace NifOsg } } - osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::ImageManager* textureManager) + osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager) { if (nif->numRoots() < 1) nif->fail("Found no root nodes"); @@ -356,7 +356,7 @@ namespace NifOsg osg::ref_ptr textkeys (new TextKeyMapHolder); - osg::ref_ptr created = handleNode(nifNode, NULL, textureManager, std::vector(), 0, 0, false, &textkeys->mTextKeys); + osg::ref_ptr created = handleNode(nifNode, NULL, imageManager, std::vector(), 0, 0, false, &textkeys->mTextKeys); if (nif->getUseSkinning()) { @@ -369,13 +369,13 @@ namespace NifOsg return created; } - void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* textureManager, std::vector& boundTextures, int animflags) + void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { const Nif::PropertyList& props = nifNode->props; for (size_t i = 0; i handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* textureManager, + osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, std::vector boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { osg::ref_ptr node = new osg::MatrixTransform(nifNode->trafo.toMatrix()); @@ -542,7 +542,7 @@ namespace NifOsg osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; - applyNodeProperties(nifNode, node, composite, textureManager, boundTextures, animflags); + applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) { @@ -588,7 +588,7 @@ namespace NifOsg { if(!children[i].empty()) { - handleNode(children[i].getPtr(), node, textureManager, boundTextures, animflags, particleflags, skipMeshes, textKeys, rootNode); + handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, particleflags, skipMeshes, textKeys, rootNode); } } } @@ -682,7 +682,7 @@ namespace NifOsg } } - void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* textureManager, osg::StateSet *stateset, int animflags) + void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, osg::StateSet *stateset, int animflags) { for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { @@ -708,8 +708,8 @@ namespace NifOsg wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); } - std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS()); - osg::ref_ptr texture (new osg::Texture2D(textureManager->getImage(filename))); + std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS()); + osg::ref_ptr texture (new osg::Texture2D(imageManager->getImage(filename))); texture->setWrap(osg::Texture::WRAP_S, wrapS); texture->setWrap(osg::Texture::WRAP_T, wrapT); textures.push_back(texture); @@ -1244,7 +1244,7 @@ namespace NifOsg } void handleProperty(const Nif::Property *property, - osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* textureManager, std::vector& boundTextures, int animflags) + osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { switch (property->recType) { @@ -1372,14 +1372,14 @@ namespace NifOsg continue; } - std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS()); + std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS()); unsigned int clamp = static_cast(tex.clamp); int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; // create a new texture, will later attempt to share using the SharedStateManager - osg::ref_ptr texture2d (new osg::Texture2D(textureManager->getImage(filename))); + osg::ref_ptr texture2d (new osg::Texture2D(imageManager->getImage(filename))); texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); @@ -1423,7 +1423,7 @@ namespace NifOsg boundTextures.push_back(tex.uvSet); } - handleTextureControllers(texprop, composite, textureManager, stateset, animflags); + handleTextureControllers(texprop, composite, imageManager, stateset, animflags); } break; } @@ -1543,10 +1543,10 @@ namespace NifOsg }; - osg::ref_ptr Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* textureManager) + osg::ref_ptr Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager) { LoaderImpl impl(file->getFilename()); - return impl.load(file, textureManager); + return impl.load(file, imageManager); } void Loader::loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target) diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index afc912ed3..d2d5e7b2d 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -62,7 +62,7 @@ namespace NifOsg { public: /// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton if so. - static osg::ref_ptr load(Nif::NIFFilePtr file, Resource::ImageManager* textureManager); + static osg::ref_ptr load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager); /// Load keyframe controllers from the given kf file. static void loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target); diff --git a/components/resource/imagemanager.hpp b/components/resource/imagemanager.hpp index ee0c0f2e2..3b2bacc13 100644 --- a/components/resource/imagemanager.hpp +++ b/components/resource/imagemanager.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_COMPONENTS_RESOURCE_TEXTUREMANAGER_H -#define OPENMW_COMPONENTS_RESOURCE_TEXTUREMANAGER_H +#ifndef OPENMW_COMPONENTS_RESOURCE_IMAGEMANAGER_H +#define OPENMW_COMPONENTS_RESOURCE_IMAGEMANAGER_H #include #include diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 89a4b2cb3..d39a723d6 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -13,8 +13,8 @@ namespace Resource { mNifFileManager.reset(new NifFileManager(vfs)); mKeyframeManager.reset(new KeyframeManager(vfs)); - mTextureManager.reset(new ImageManager(vfs)); - mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get())); + mImageManager.reset(new ImageManager(vfs)); + mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get())); } ResourceSystem::~ResourceSystem() @@ -27,9 +27,9 @@ namespace Resource return mSceneManager.get(); } - ImageManager* ResourceSystem::getTextureManager() + ImageManager* ResourceSystem::getImageManager() { - return mTextureManager.get(); + return mImageManager.get(); } NifFileManager* ResourceSystem::getNifFileManager() diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 7c4d83b7d..13a96e8c7 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -26,7 +26,7 @@ namespace Resource ~ResourceSystem(); SceneManager* getSceneManager(); - ImageManager* getTextureManager(); + ImageManager* getImageManager(); NifFileManager* getNifFileManager(); KeyframeManager* getKeyframeManager(); @@ -37,7 +37,7 @@ namespace Resource private: std::auto_ptr mSceneManager; - std::auto_ptr mTextureManager; + std::auto_ptr mImageManager; std::auto_ptr mNifFileManager; std::auto_ptr mKeyframeManager; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 287186887..dd4847877 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -227,9 +227,9 @@ namespace Resource - SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* textureManager, Resource::NifFileManager* nifFileManager) + SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) : mVFS(vfs) - , mTextureManager(textureManager) + , mImageManager(imageManager) , mNifFileManager(nifFileManager) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) , mMagFilter(osg::Texture::LINEAR) @@ -249,8 +249,8 @@ namespace Resource class ImageReadCallback : public osgDB::ReadFileCallback { public: - ImageReadCallback(Resource::ImageManager* textureMgr) - : mTextureManager(textureMgr) + ImageReadCallback(Resource::ImageManager* imageMgr) + : mImageManager(imageMgr) { } @@ -258,7 +258,7 @@ namespace Resource { try { - return osgDB::ReaderWriter::ReadResult(mTextureManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED); + return osgDB::ReaderWriter::ReadResult(mImageManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED); } catch (std::exception& e) { @@ -267,7 +267,7 @@ namespace Resource } private: - Resource::ImageManager* mTextureManager; + Resource::ImageManager* mImageManager; }; std::string getFileExtension(const std::string& file) @@ -278,11 +278,11 @@ namespace Resource return std::string(); } - osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* textureMgr, Resource::NifFileManager* nifFileManager) + osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) { std::string ext = getFileExtension(normalizedFilename); if (ext == "nif") - return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), textureMgr); + return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager); else { osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); @@ -297,7 +297,7 @@ namespace Resource // Set a ReadFileCallback so that image files referenced in the model are read from our virtual file system instead of the osgDB. // Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary. // but findFileCallback does not support virtual files, so we can't implement it. - options->setReadFileCallback(new ImageReadCallback(textureMgr)); + options->setReadFileCallback(new ImageReadCallback(imageManager)); osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options); if (!result.success()) @@ -323,7 +323,7 @@ namespace Resource { Files::IStreamPtr file = mVFS->get(normalized); - loaded = load(file, normalized, mTextureManager, mNifFileManager); + loaded = load(file, normalized, mImageManager, mNifFileManager); } catch (std::exception& e) { @@ -336,7 +336,7 @@ namespace Resource { std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl; Files::IStreamPtr file = mVFS->get(normalized); - loaded = load(file, normalized, mTextureManager, mNifFileManager); + loaded = load(file, normalized, mImageManager, mNifFileManager); break; } } @@ -408,9 +408,9 @@ namespace Resource return mVFS; } - Resource::ImageManager* SceneManager::getTextureManager() + Resource::ImageManager* SceneManager::getImageManager() { - return mTextureManager; + return mImageManager; } void SceneManager::setParticleSystemMask(unsigned int mask) diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 6eadf7b45..3dd37111d 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -36,7 +36,7 @@ namespace Resource class SceneManager { public: - SceneManager(const VFS::Manager* vfs, Resource::ImageManager* textureManager, Resource::NifFileManager* nifFileManager); + SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager); ~SceneManager(); /// Get a read-only copy of this scene "template" @@ -70,7 +70,7 @@ namespace Resource const VFS::Manager* getVFS() const; - Resource::ImageManager* getTextureManager(); + Resource::ImageManager* getImageManager(); /// @param mask The node mask to apply to loaded particle system nodes. void setParticleSystemMask(unsigned int mask); @@ -89,7 +89,7 @@ namespace Resource private: const VFS::Manager* mVFS; - Resource::ImageManager* mTextureManager; + Resource::ImageManager* mImageManager; Resource::NifFileManager* mNifFileManager; osg::Texture::FilterMode mMinFilter; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 094023f38..2efbf1b9e 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -142,7 +142,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu osg::ref_ptr tex = mTextureCache[it->mDiffuseMap]; if (!tex) { - tex = new osg::Texture2D(mResourceSystem->getTextureManager()->getImage(it->mDiffuseMap)); + tex = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap)); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); mTextureCache[it->mDiffuseMap] = tex; From 499beda6656e37b45aa35549126023fd4898ceb2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:20:13 +0100 Subject: [PATCH 12/66] Clear terrain texture cache before applying filter settings --- apps/openmw/mwrender/renderingmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d1b5f6fda..9b7ced4b5 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -783,6 +783,9 @@ namespace MWRender void RenderingManager::updateTextureFiltering() { + if (mTerrain.get()) + mTerrain->clearCache(); + mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), From 9e5225bb6f952b50402063783374ffcd7a91be23 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:21:54 +0100 Subject: [PATCH 13/66] Do not unref a Texture's image data after applying it --- apps/openmw/engine.cpp | 2 +- components/terrain/terraingrid.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d96bf16e4..d30d0961a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -448,7 +448,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get())); - mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(true); + mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(false); // keep to Off for now to allow better state sharing mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), Settings::Manager::getString("texture min filter", "General"), diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 2efbf1b9e..eb085ca42 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -166,10 +166,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back()); } - // SharedStatemanager->share(textureCompileDummy); - - // Remove unrefImageDataAfterApply for better state sharing - // use texture coordinates for both texture units, the layer texture and blend texture for (unsigned int i=0; i<2; ++i) geometry->setTexCoordArray(i, mCache.getUVBuffer()); From a72af4a1a38e9a39057dce2951cd6a0817025810 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:25:01 +0100 Subject: [PATCH 14/66] cout that should be cerr --- apps/openmw/mwworld/localscripts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 7ccc213c4..f0174ac52 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -101,7 +101,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) for (std::list >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter) if (iter->second==ptr) { - std::cout << "warning, tried to add local script twice for " << ptr.getCellRef().getRefId() << std::endl; + std::cerr << "warning, tried to add local script twice for " << ptr.getCellRef().getRefId() << std::endl; remove(ptr); break; } From 6c1c653cbab3fe0228303ccf50c40c93dfa9c370 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:31:59 +0100 Subject: [PATCH 15/66] Use the osgDB::ObjectCache in ImageManager Should be thread safe now. --- components/resource/imagemanager.cpp | 16 ++++++++++++---- components/resource/imagemanager.hpp | 5 +++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index e1c0572c7..bd422fe2a 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -6,6 +6,8 @@ #include +#include "objectcache.hpp" + #ifdef OSG_LIBRARY_STATIC // This list of plugins should match with the list in the top-level CMakelists.txt. USE_OSGPLUGIN(png) @@ -41,6 +43,7 @@ namespace Resource ImageManager::ImageManager(const VFS::Manager *vfs) : mVFS(vfs) + , mCache(new osgDB::ObjectCache) , mWarningImage(createWarningImage()) , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba")) { @@ -88,9 +91,10 @@ namespace Resource { std::string normalized = filename; mVFS->normalizeFilename(normalized); - std::map >::iterator found = mImages.find(normalized); - if (found != mImages.end()) - return found->second; + + osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); + if (obj) + return osg::ref_ptr(static_cast(obj.get())); else { Files::IStreamPtr stream; @@ -101,6 +105,7 @@ namespace Resource catch (std::exception& e) { std::cerr << "Failed to open image: " << e.what() << std::endl; + mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -112,6 +117,7 @@ namespace Resource if (!reader) { std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; + mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -119,16 +125,18 @@ namespace Resource if (!result.success()) { std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; + mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } osg::Image* image = result.getImage(); if (!checkSupported(image, filename)) { + mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } - mImages.insert(std::make_pair(normalized, image)); + mCache->addEntryToObjectCache(normalized, image); return image; } } diff --git a/components/resource/imagemanager.hpp b/components/resource/imagemanager.hpp index 3b2bacc13..bc1d7b4e4 100644 --- a/components/resource/imagemanager.hpp +++ b/components/resource/imagemanager.hpp @@ -21,12 +21,14 @@ namespace VFS namespace osgDB { class Options; + class ObjectCache; } namespace Resource { /// @brief Handles loading/caching of Images. + /// @note May be used from any thread. class ImageManager { public: @@ -44,8 +46,7 @@ namespace Resource private: const VFS::Manager* mVFS; - // TODO: use ObjectCache - std::map > mImages; + osg::ref_ptr mCache; osg::ref_ptr mWarningImage; osg::ref_ptr mOptions; From 909c4d96b64e5fa6f85a959f59964927a28730b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 5 Feb 2016 23:59:37 +0100 Subject: [PATCH 16/66] Use the osgDB::ObjectCache in BulletShapeManager --- components/resource/bulletshape.cpp | 8 ++++++++ components/resource/bulletshape.hpp | 8 ++++++-- components/resource/bulletshapemanager.cpp | 21 ++++++++++++++------- components/resource/bulletshapemanager.hpp | 8 ++++++-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/components/resource/bulletshape.cpp b/components/resource/bulletshape.cpp index 0cbc63a22..baff86a79 100644 --- a/components/resource/bulletshape.cpp +++ b/components/resource/bulletshape.cpp @@ -17,6 +17,14 @@ BulletShape::BulletShape() } +BulletShape::BulletShape(const BulletShape ©, const osg::CopyOp ©op) + : mCollisionShape(duplicateCollisionShape(copy.mCollisionShape)) + , mCollisionBoxHalfExtents(copy.mCollisionBoxHalfExtents) + , mCollisionBoxTranslate(copy.mCollisionBoxTranslate) + , mAnimatedShapes(copy.mAnimatedShapes) +{ +} + BulletShape::~BulletShape() { deleteShape(mCollisionShape); diff --git a/components/resource/bulletshape.hpp b/components/resource/bulletshape.hpp index cfae27eac..a007bad31 100644 --- a/components/resource/bulletshape.hpp +++ b/components/resource/bulletshape.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include @@ -15,12 +15,15 @@ namespace Resource { class BulletShapeInstance; - class BulletShape : public osg::Referenced + class BulletShape : public osg::Object { public: BulletShape(); + BulletShape(const BulletShape& copy, const osg::CopyOp& copyop); virtual ~BulletShape(); + META_Object(Resource, BulletShape) + btCollisionShape* mCollisionShape; // Used for actors. Note, ideally actors would use a separate loader - as it is @@ -42,6 +45,7 @@ namespace Resource btCollisionShape* getCollisionShape(); private: + void deleteShape(btCollisionShape* shape); }; diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 4cbe62f3c..2ab7b243a 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -13,6 +13,7 @@ #include "bulletshape.hpp" #include "scenemanager.hpp" #include "niffilemanager.hpp" +#include "objectcache.hpp" namespace Resource @@ -99,6 +100,7 @@ BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sc : mVFS(vfs) , mSceneManager(sceneMgr) , mNifFileManager(nifFileManager) + , mCache(new osgDB::ObjectCache) { } @@ -114,8 +116,10 @@ osg::ref_ptr BulletShapeManager::createInstance(const std:: mVFS->normalizeFilename(normalized); osg::ref_ptr shape; - Index::iterator it = mIndex.find(normalized); - if (it == mIndex.end()) + osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); + if (obj) + shape = osg::ref_ptr(static_cast(obj.get())); + else { size_t extPos = normalized.find_last_of('.'); std::string ext; @@ -137,16 +141,19 @@ osg::ref_ptr BulletShapeManager::createInstance(const std:: node->accept(visitor); shape = visitor.getShape(); if (!shape) + { + mCache->addEntryToObjectCache(normalized, NULL); return osg::ref_ptr(); + } } - mIndex[normalized] = shape; + mCache->addEntryToObjectCache(normalized, shape); } - else - shape = it->second; - osg::ref_ptr instance = shape->makeInstance(); - return instance; + if (shape) + return shape->makeInstance(); + else + return osg::ref_ptr(); } } diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index ac1523495..d9293896b 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -13,6 +13,11 @@ namespace VFS class Manager; } +namespace osgDB +{ + class ObjectCache; +} + namespace Resource { class SceneManager; @@ -34,8 +39,7 @@ namespace Resource SceneManager* mSceneManager; NifFileManager* mNifFileManager; - typedef std::map > Index; - Index mIndex; + osg::ref_ptr mCache; }; } From ea1efaac0cc5b7d228a90a99757594fe25e10cab Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 6 Feb 2016 00:15:12 +0100 Subject: [PATCH 17/66] Use the osgDB::ObjectCache in SceneManager, cleanup --- components/resource/bulletshapemanager.hpp | 3 +++ components/resource/keyframemanager.hpp | 2 +- components/resource/niffilemanager.hpp | 2 +- components/resource/scenemanager.cpp | 27 +++++++++------------- components/resource/scenemanager.hpp | 23 ++++++++++-------- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index d9293896b..c8db8849e 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -26,6 +26,9 @@ namespace Resource class BulletShape; class BulletShapeInstance; + /// 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 { public: diff --git a/components/resource/keyframemanager.hpp b/components/resource/keyframemanager.hpp index 5032d0e38..5a5cb3628 100644 --- a/components/resource/keyframemanager.hpp +++ b/components/resource/keyframemanager.hpp @@ -23,6 +23,7 @@ namespace Resource { /// @brief Managing of keyframe resources + /// @note May be used from any thread. class KeyframeManager { public: @@ -32,7 +33,6 @@ namespace Resource void clearCache(); /// Retrieve a read-only keyframe resource by name (case-insensitive). - /// @note This method is safe to call from any thread. /// @note Throws an exception if the resource is not found. osg::ref_ptr get(const std::string& name); diff --git a/components/resource/niffilemanager.hpp b/components/resource/niffilemanager.hpp index 90ad9fc29..4551cf227 100644 --- a/components/resource/niffilemanager.hpp +++ b/components/resource/niffilemanager.hpp @@ -19,7 +19,7 @@ namespace Resource { /// @brief Handles caching of NIFFiles. - /// @note The NifFileManager is completely thread safe. + /// @note May be used from any thread. class NifFileManager { public: diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index dd4847877..95e03b389 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -26,10 +26,12 @@ #include "imagemanager.hpp" #include "niffilemanager.hpp" +#include "objectcache.hpp" namespace { + /// @todo Do this in updateCallback so that animations are accounted for. class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor { public: @@ -236,6 +238,7 @@ namespace Resource , mMaxAnisotropy(1) , mUnRefImageDataAfterApply(false) , mParticleSystemMask(~0u) + , mCache(new osgDB::ObjectCache) { } @@ -315,8 +318,10 @@ namespace Resource std::string normalized = name; mVFS->normalizeFilename(normalized); - Index::iterator it = mIndex.find(normalized); - if (it == mIndex.end()) + osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); + if (obj) + return osg::ref_ptr(static_cast(obj.get())); + else { osg::ref_ptr loaded; try @@ -357,11 +362,9 @@ namespace Resource if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); - mIndex[normalized] = loaded; + mCache->addEntryToObjectCache(normalized, loaded); return loaded; } - else - return it->second; } osg::ref_ptr SceneManager::createInstance(const std::string &name) @@ -386,10 +389,7 @@ namespace Resource void SceneManager::releaseGLObjects(osg::State *state) { - for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it) - { - it->second->releaseGLObjects(state); - } + mCache->releaseGLObjects(state); } void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico) @@ -458,6 +458,8 @@ namespace Resource mMagFilter = mag; mMaxAnisotropy = std::max(1, maxAnisotropy); + mCache->clear(); + SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor (mMinFilter, mMagFilter, mMaxAnisotropy); SetFilterSettingsVisitor setFilterSettingsVisitor (mMinFilter, mMagFilter, mMaxAnisotropy); if (viewer && viewer->getSceneData()) @@ -466,13 +468,6 @@ namespace Resource viewer->getSceneData()->accept(setFilterSettingsVisitor); } - for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it) - { - osg::Node* node = it->second; - node->accept(setFilterSettingsControllerVisitor); - node->accept(setFilterSettingsVisitor); - } - if(viewer) viewer->startThreading(); } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 3dd37111d..32bd80660 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -24,6 +24,11 @@ namespace osgUtil class IncrementalCompileOperation; } +namespace osgDB +{ + class ObjectCache; +} + namespace osgViewer { class Viewer; @@ -32,7 +37,8 @@ namespace osgViewer namespace Resource { - /// @brief Handles loading and caching of scenes, e.g. NIF 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. class SceneManager { public: @@ -42,20 +48,24 @@ namespace Resource /// Get a read-only copy of this scene "template" /// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead. /// If even the error marker mesh can not be found, an exception is thrown. + /// @note Thread safe. osg::ref_ptr getTemplate(const std::string& name); /// Create an instance of the given scene template /// @see getTemplate + /// @note Thread safe. osg::ref_ptr createInstance(const std::string& name); /// Create 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); /// Attach the given scene instance to the given parent node /// @note You should have the parentNode in its intended position before calling this method, /// so that world space particles of the \a instance get transformed correctly. /// @note Assumes the given instance was not attached to any parents before. + /// @note Not thread safe, unless parentNode is not part of the main scene graph yet. void attachTo(osg::Node* instance, osg::Group* parentNode) const; /// Manually release created OpenGL objects for the given graphics context. This may be required @@ -75,11 +85,12 @@ namespace Resource /// @param mask The node mask to apply to loaded particle system nodes. void setParticleSystemMask(unsigned int mask); + /// @param viewer used to apply the new filter settings to the existing scene graph. If there is no scene yet, you can pass a NULL viewer. void setFilterSettings(const std::string &magfilter, const std::string &minfilter, const std::string &mipmap, int maxAnisotropy, osgViewer::Viewer *viewer); - /// Apply filter settings to the given texture. Note, when loading an object through this scene manager (i.e. calling getTemplate / createInstance) + /// Apply filter settings to the given texture. Note, when loading an object through this scene manager (i.e. calling getTemplate or createInstance) /// the filter settings are applied automatically. This method is provided for textures that were created outside of the SceneManager. void applyFilterSettings (osg::Texture* tex); @@ -101,16 +112,10 @@ namespace Resource unsigned int mParticleSystemMask; - // observer_ptr? - typedef std::map > Index; - Index mIndex; + osg::ref_ptr mCache; SceneManager(const SceneManager&); void operator = (const SceneManager&); - - /// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first! - void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy); - }; } From df57d4bfba49b41b692c7db85b9ef774fa772570 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 6 Feb 2016 16:57:54 +0100 Subject: [PATCH 18/66] Use a common base class for resource managers Implement updateCache to delete unreferenced cached objects when they have not been referenced for a while. --- apps/openmw/mwphysics/physicssystem.cpp | 5 +++ apps/openmw/mwphysics/physicssystem.hpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 2 +- components/CMakeLists.txt | 2 +- components/resource/bulletshapemanager.cpp | 3 +- components/resource/bulletshapemanager.hpp | 16 +------- components/resource/imagemanager.cpp | 3 +- components/resource/imagemanager.hpp | 16 ++------ components/resource/keyframemanager.cpp | 3 +- components/resource/keyframemanager.hpp | 19 +--------- components/resource/niffilemanager.cpp | 10 +---- components/resource/niffilemanager.hpp | 20 +--------- components/resource/resourcemanager.cpp | 34 +++++++++++++++++ components/resource/resourcemanager.hpp | 43 ++++++++++++++++++++++ components/resource/resourcesystem.cpp | 29 ++++++++++++++- components/resource/resourcesystem.hpp | 17 ++++++++- components/resource/scenemanager.cpp | 9 +---- components/resource/scenemanager.hpp | 19 ++-------- components/vfs/manager.hpp | 6 +++ 19 files changed, 152 insertions(+), 105 deletions(-) create mode 100644 components/resource/resourcemanager.cpp create mode 100644 components/resource/resourcemanager.hpp diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index c99456ae5..11d6d286a 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 f53d7e3d9..4263bc0ec 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 9b7ced4b5..d4199d5f6 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 0b54ddeb7..42323a68a 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 2ab7b243a..b5581cce2 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 c8db8849e..576268f75 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 bd422fe2a..79da7d7ab 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 bc1d7b4e4..8d9ad2c32 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 7e948dcb0..4392f84c1 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 5a5cb3628..1c2c219bb 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 1d8019b69..3c7437520 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 4551cf227..4b43ff24b 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 000000000..60233baa0 --- /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 000000000..e45a2e9cb --- /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 d39a723d6..f499c0016 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 13a96e8c7..30607432d 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 95e03b389..4ed14241a 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 32bd80660..173131e66 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 f74914977..6592a65a8 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: From b7e69cbc64fafd1f8ae255c4425f0e3b5cdcdd6e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 6 Feb 2016 22:29:06 +0100 Subject: [PATCH 19/66] Refactor WorkQueue, merge WorkTicket and WorkItem Allow the caller to hold on to the WorkItem. This makes it possible for a derived WorkItem to store the result of the work within the WorkItem itself. --- components/sceneutil/workqueue.cpp | 39 ++++++++--------- components/sceneutil/workqueue.hpp | 69 ++++++++++++++---------------- 2 files changed, 49 insertions(+), 59 deletions(-) diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 26a392be4..bb1d1f1f7 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -1,9 +1,11 @@ #include "workqueue.hpp" +#include + namespace SceneUtil { -void WorkTicket::waitTillDone() +void WorkItem::waitTillDone() { if (mDone > 0) return; @@ -15,7 +17,7 @@ void WorkTicket::waitTillDone() } } -void WorkTicket::signalDone() +void WorkItem::signalDone() { { OpenThreads::ScopedLock lock(mMutex); @@ -25,23 +27,16 @@ void WorkTicket::signalDone() } WorkItem::WorkItem() - : mTicket(new WorkTicket) { - mTicket->setThreadSafeRefUnref(true); } WorkItem::~WorkItem() { } -void WorkItem::doWork() -{ - mTicket->signalDone(); -} - -osg::ref_ptr WorkItem::getTicket() +bool WorkItem::isDone() const { - return mTicket; + return (mDone > 0); } WorkQueue::WorkQueue(int workerThreads) @@ -60,11 +55,7 @@ WorkQueue::~WorkQueue() { OpenThreads::ScopedLock lock(mMutex); while (!mQueue.empty()) - { - WorkItem* item = mQueue.front(); - delete item; mQueue.pop(); - } mIsReleased = true; mCondition.broadcast(); } @@ -76,16 +67,20 @@ WorkQueue::~WorkQueue() } } -osg::ref_ptr WorkQueue::addWorkItem(WorkItem *item) +void WorkQueue::addWorkItem(osg::ref_ptr item) { - osg::ref_ptr ticket = item->getTicket(); + if (item->isDone()) + { + std::cerr << "warning, trying to add a work item that is already completed" << std::endl; + return; + } + OpenThreads::ScopedLock lock(mMutex); mQueue.push(item); mCondition.signal(); - return ticket; } -WorkItem *WorkQueue::removeWorkItem() +osg::ref_ptr WorkQueue::removeWorkItem() { OpenThreads::ScopedLock lock(mMutex); while (mQueue.empty() && !mIsReleased) @@ -94,7 +89,7 @@ WorkItem *WorkQueue::removeWorkItem() } if (mQueue.size()) { - WorkItem* item = mQueue.front(); + osg::ref_ptr item = mQueue.front(); mQueue.pop(); return item; } @@ -111,11 +106,11 @@ void WorkThread::run() { while (true) { - WorkItem* item = mWorkQueue->removeWorkItem(); + osg::ref_ptr item = mWorkQueue->removeWorkItem(); if (!item) return; item->doWork(); - delete item; + item->signalDone(); } } diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index 492bbd090..06489dfbb 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -14,69 +14,53 @@ namespace SceneUtil { - class WorkTicket : public osg::Referenced - { - public: - void waitTillDone(); - - void signalDone(); - - private: - OpenThreads::Atomic mDone; - OpenThreads::Mutex mMutex; - OpenThreads::Condition mCondition; - }; - - class WorkItem + class WorkItem : public osg::Referenced { public: WorkItem(); virtual ~WorkItem(); /// Override in a derived WorkItem to perform actual work. - /// By default, just signals the ticket that the work is done. - virtual void doWork(); + virtual void doWork() {} + + bool isDone() const; - osg::ref_ptr getTicket(); + /// Wait until the work is completed. Usually called from the main thread. + void waitTillDone(); + + /// Internal use by the WorkQueue. + void signalDone(); protected: - osg::ref_ptr mTicket; + OpenThreads::Atomic mDone; + OpenThreads::Mutex mMutex; + OpenThreads::Condition mCondition; }; class WorkQueue; - - class WorkThread : public OpenThreads::Thread - { - public: - WorkThread(WorkQueue* workQueue); - - virtual void run(); - - private: - WorkQueue* mWorkQueue; - }; + class WorkThread; /// @brief A work queue that users can push work items onto, to be completed by one or more background threads. - class WorkQueue + /// @note Work items will be processed in the order that they were given in, however + /// if multiple work threads are involved then it is possible for a later item to complete before earlier items. + class WorkQueue : public osg::Referenced { public: WorkQueue(int numWorkerThreads=1); ~WorkQueue(); /// Add a new work item to the back of the queue. - /// @par The returned WorkTicket may be used by the caller to wait until the work is complete. - osg::ref_ptr addWorkItem(WorkItem* item); + /// @par The work item's waitTillDone() method may be used by the caller to wait until the work is complete. + void addWorkItem(osg::ref_ptr item); /// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added. /// If the workqueue is in the process of being destroyed, may return NULL. - /// @note The caller must free the returned WorkItem - WorkItem* removeWorkItem(); - - void runThread(); + /// @par Used internally by the WorkThread. + osg::ref_ptr removeWorkItem(); private: bool mIsReleased; - std::queue mQueue; + std::queue > mQueue; OpenThreads::Mutex mMutex; OpenThreads::Condition mCondition; @@ -84,6 +68,17 @@ namespace SceneUtil std::vector mThreads; }; + /// Internally used by WorkQueue. + class WorkThread : public OpenThreads::Thread + { + public: + WorkThread(WorkQueue* workQueue); + + virtual void run(); + + private: + WorkQueue* mWorkQueue; + }; } From e055ae094aeab055caecb04c3d898656c656406b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 6 Feb 2016 23:30:41 +0100 Subject: [PATCH 20/66] Improve const-correctness in BulletShapeManager Sadly, two const_cast's are needed to work around Bullet API quirks. --- components/resource/bulletshape.cpp | 26 ++++++++++------------ components/resource/bulletshape.hpp | 8 +++---- components/resource/bulletshapemanager.cpp | 9 ++++++-- components/resource/bulletshapemanager.hpp | 3 +++ 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/components/resource/bulletshape.cpp b/components/resource/bulletshape.cpp index baff86a79..6855429c3 100644 --- a/components/resource/bulletshape.cpp +++ b/components/resource/bulletshape.cpp @@ -45,11 +45,11 @@ void BulletShape::deleteShape(btCollisionShape* shape) } } -btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const +btCollisionShape* BulletShape::duplicateCollisionShape(const btCollisionShape *shape) const { if(shape->isCompound()) { - btCompoundShape *comp = static_cast(shape); + const btCompoundShape *comp = static_cast(shape); btCompoundShape *newShape = new btCompoundShape; int numShapes = comp->getNumChildShapes(); @@ -63,29 +63,27 @@ btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) return newShape; } - if(btBvhTriangleMeshShape* trishape = dynamic_cast(shape)) + if(const btBvhTriangleMeshShape* trishape = dynamic_cast(shape)) { #if BT_BULLET_VERSION >= 283 - btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(trishape, btVector3(1.f, 1.f, 1.f)); + btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(const_cast(trishape), btVector3(1.f, 1.f, 1.f)); #else // work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions - btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); + const btTriangleMesh* oldMesh = static_cast(trishape->getMeshInterface()); btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); // Do not build a new bvh (not needed, since it's the same as the original shape's bvh) - bool buildBvh = true; - if (trishape->getOptimizedBvh()) - buildBvh = false; - TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, buildBvh); + btOptimizedBvh* bvh = const_cast(trishape)->getOptimizedBvh(); + TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, bvh == NULL); // Set original shape's bvh via pointer // The pointer is safe because the BulletShapeInstance keeps a ref_ptr to the original BulletShape - if (!buildBvh) - newShape->setOptimizedBvh(trishape->getOptimizedBvh()); + if (bvh) + newShape->setOptimizedBvh(bvh); #endif return newShape; } - if (btBoxShape* boxshape = dynamic_cast(shape)) + if (const btBoxShape* boxshape = dynamic_cast(shape)) { return new btBoxShape(*boxshape); } @@ -98,13 +96,13 @@ btCollisionShape *BulletShape::getCollisionShape() return mCollisionShape; } -osg::ref_ptr BulletShape::makeInstance() +osg::ref_ptr BulletShape::makeInstance() const { osg::ref_ptr instance (new BulletShapeInstance(this)); return instance; } -BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) +BulletShapeInstance::BulletShapeInstance(osg::ref_ptr source) : BulletShape() , mSource(source) { diff --git a/components/resource/bulletshape.hpp b/components/resource/bulletshape.hpp index a007bad31..a418bb28c 100644 --- a/components/resource/bulletshape.hpp +++ b/components/resource/bulletshape.hpp @@ -38,9 +38,9 @@ namespace Resource // we store the node's record index mapped to the child index of the shape in the btCompoundShape. std::map mAnimatedShapes; - osg::ref_ptr makeInstance(); + osg::ref_ptr makeInstance() const; - btCollisionShape* duplicateCollisionShape(btCollisionShape* shape) const; + btCollisionShape* duplicateCollisionShape(const btCollisionShape* shape) const; btCollisionShape* getCollisionShape(); @@ -55,10 +55,10 @@ namespace Resource class BulletShapeInstance : public BulletShape { public: - BulletShapeInstance(osg::ref_ptr source); + BulletShapeInstance(osg::ref_ptr source); private: - osg::ref_ptr mSource; + osg::ref_ptr mSource; }; // Subclass btBhvTriangleMeshShape to auto-delete the meshInterface diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index b5581cce2..cb57d686f 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -109,7 +109,7 @@ BulletShapeManager::~BulletShapeManager() } -osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) +osg::ref_ptr BulletShapeManager::getShape(const std::string &name) { std::string normalized = name; mVFS->normalizeFilename(normalized); @@ -142,13 +142,18 @@ osg::ref_ptr BulletShapeManager::createInstance(const std:: if (!shape) { mCache->addEntryToObjectCache(normalized, NULL); - return osg::ref_ptr(); + return osg::ref_ptr(); } } mCache->addEntryToObjectCache(normalized, shape); } + return shape; +} +osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) +{ + osg::ref_ptr shape = getShape(name); if (shape) return shape->makeInstance(); else diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index 576268f75..4c0cb1fd2 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -26,6 +26,9 @@ namespace Resource BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); ~BulletShapeManager(); + osg::ref_ptr getShape(const std::string& name); + + /// Shorthand for getShape(name)->makeInstance(); osg::ref_ptr createInstance(const std::string& name); private: From 6f9ca0f68fd76bfd1abef2f9e5ae0131b8a64e89 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 00:07:02 +0100 Subject: [PATCH 21/66] Add basic cell preloader class Not properly in use yet, but seems to be working. --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwphysics/physicssystem.cpp | 5 + apps/openmw/mwphysics/physicssystem.hpp | 2 + apps/openmw/mwworld/cellpreloader.cpp | 148 ++++++++++++++++++++++++ apps/openmw/mwworld/cellpreloader.hpp | 60 ++++++++++ apps/openmw/mwworld/cellstore.cpp | 5 + apps/openmw/mwworld/cellstore.hpp | 3 + components/CMakeLists.txt | 4 +- components/resource/resourcesystem.cpp | 3 + 9 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwworld/cellpreloader.cpp create mode 100644 apps/openmw/mwworld/cellpreloader.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 02cf6c87d..f2540c739 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -67,6 +67,7 @@ add_openmw_dir (mwworld actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager + cellpreloader ) add_openmw_dir (mwphysics diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 11d6d286a..6417968d3 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -690,6 +690,11 @@ namespace MWPhysics delete mBroadphase; } + Resource::BulletShapeManager *PhysicsSystem::getShapeManager() + { + return mShapeManager.get(); + } + bool PhysicsSystem::toggleDebugRendering() { mDebugDrawEnabled = !mDebugDrawEnabled; diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 4263bc0ec..a866717c7 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -53,6 +53,8 @@ namespace MWPhysics PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode); ~PhysicsSystem (); + Resource::BulletShapeManager* getShapeManager(); + void enableWater(float height); void setWaterHeight(float height); void disableWater(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp new file mode 100644 index 000000000..dd6850aba --- /dev/null +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -0,0 +1,148 @@ +#include "cellpreloader.hpp" + +#include + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "cellstore.hpp" +#include "manualref.hpp" +#include "class.hpp" + +namespace MWWorld +{ + + struct ListModelsVisitor + { + ListModelsVisitor(std::vector& out) + : mOut(out) + { + } + + virtual bool operator()(const MWWorld::ConstPtr& ptr) + { + std::string model = ptr.getClass().getModel(ptr); + if (!model.empty()) + mOut.push_back(model); + return true; + } + + std::vector& mOut; + }; + + class PreloadItem : public SceneUtil::WorkItem + { + public: + /// Constructor to be called from the main thread. + PreloadItem(const MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager) + : mSceneManager(sceneManager) + , mBulletShapeManager(bulletShapeManager) + { + if (cell->getState() == MWWorld::CellStore::State_Loaded) + { + ListModelsVisitor visitor (mMeshes); + cell->forEachConst(visitor); + } + else + { + const std::vector& objectIds = cell->getPreloadedIds(); + + // could possibly build the model list in the worker thread if we manage to make the Store thread safe + for (std::vector::const_iterator it = objectIds.begin(); it != objectIds.end(); ++it) + { + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), *it); + std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); + if (!model.empty()) + mMeshes.push_back(model); + } + } + } + + /// Preload work to be called from the worker thread. + virtual void doWork() + { + for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it) + { + try + { + mPreloadedNodes.push_back(mSceneManager->getTemplate(*it)); + mPreloadedShapes.push_back(mBulletShapeManager->getShape(*it)); + + // 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) + { + // ignore error for now, would spam the log too much + // error will be shown when visiting the cell + } + } + } + + private: + typedef std::vector MeshList; + MeshList mMeshes; + Resource::SceneManager* mSceneManager; + Resource::BulletShapeManager* mBulletShapeManager; + + // keep a ref to the loaded object to make sure it stays loaded as long as this cell is in the preloaded state + std::vector > mPreloadedNodes; + std::vector > mPreloadedShapes; + }; + + CellPreloader::CellPreloader(Resource::SceneManager *sceneManager, Resource::BulletShapeManager* bulletShapeManager) + : mSceneManager(sceneManager) + , mBulletShapeManager(bulletShapeManager) + { + mWorkQueue = new SceneUtil::WorkQueue; + } + + void CellPreloader::preload(const CellStore *cell, double timestamp) + { + if (!mWorkQueue) + { + std::cerr << "can't preload, no work queue set " << std::endl; + return; + } + if (cell->getState() == CellStore::State_Unloaded) + { + std::cerr << "can't preload objects for unloaded cell" << std::endl; + return; + } + + PreloadMap::iterator found = mPreloadCells.find(cell); + if (found != mPreloadCells.end()) + { + // already preloaded, nothing to do other than updating the timestamp + found->second.mTimeStamp = timestamp; + return; + } + + osg::ref_ptr item (new PreloadItem(cell, mSceneManager, mBulletShapeManager)); + mWorkQueue->addWorkItem(item); + + mPreloadCells[cell] = PreloadEntry(timestamp, item); + } + + void CellPreloader::updateCache(double timestamp) + { + // time (in seconds) to keep a preloaded cell in cache after it's no longer needed + const double expiryTime = 60.0; + + for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();) + { + if (it->second.mTimeStamp < timestamp - expiryTime) + mPreloadCells.erase(it++); + else + ++it; + } + } + + void CellPreloader::setWorkQueue(osg::ref_ptr workQueue) + { + mWorkQueue = workQueue; + } + +} diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp new file mode 100644 index 000000000..7e02cb7d1 --- /dev/null +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -0,0 +1,60 @@ +#ifndef OPENMW_MWWORLD_CELLPRELOADER_H +#define OPENMW_MWWORLD_CELLPRELOADER_H + +#include +#include +#include + +namespace Resource +{ + class SceneManager; + class BulletShapeManager; +} + +namespace MWWorld +{ + class CellStore; + + class CellPreloader + { + public: + CellPreloader(Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager); + + /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell. + /// @note The cell itself must be in State_Loaded or State_Preloaded. + void preload(const MWWorld::CellStore* cell, double timestamp); + + /// Removes preloaded cells that have not had a preload request for a while. + void updateCache(double timestamp); + + void setWorkQueue(osg::ref_ptr workQueue); + + private: + Resource::SceneManager* mSceneManager; + Resource::BulletShapeManager* mBulletShapeManager; + osg::ref_ptr mWorkQueue; + + struct PreloadEntry + { + PreloadEntry(double timestamp, osg::ref_ptr workItem) + : mTimeStamp(timestamp) + , mWorkItem(workItem) + { + } + PreloadEntry() + : mTimeStamp(0.0) + { + } + + double mTimeStamp; + osg::ref_ptr mWorkItem; + }; + typedef std::map PreloadMap; + + // Cells that are currently being preloaded, or have already finished preloading + PreloadMap mPreloadCells; + }; + +} + +#endif diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5c8d07f86..dcaa73e93 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -333,6 +333,11 @@ namespace MWWorld return mState; } + const std::vector &CellStore::getPreloadedIds() const + { + return mIds; + } + bool CellStore::hasState() const { return mHasState; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 2f483f0ba..d753c9745 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -204,6 +204,9 @@ namespace MWWorld State getState() const; + const std::vector& getPreloadedIds() const; + ///< Get Ids of objects in this cell, only valid in State_Preloaded + bool hasState() const; ///< Does this cell have state that needs to be stored in a saved game file? diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 42323a68a..26e8f7b6a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -46,9 +46,7 @@ add_component_dir (resource add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller - lightmanager lightutil positionattitudetransform - # not used yet - #workqueue + lightmanager lightutil positionattitudetransform workqueue ) add_component_dir (nif diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index f499c0016..68ed13644 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -54,6 +54,9 @@ namespace Resource void ResourceSystem::updateCache(double referenceTime) { + // TODO: call updateCache from the worker thread so the main thread isn't held up by the delete operations + // change ObjectCache to not hold lock while the unref happens + osg::Timer timer; for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->updateCache(referenceTime); From d855a13b4435d989ebf46346e71437782c231a7f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 00:36:31 +0100 Subject: [PATCH 22/66] Clear the resource cache from the worker thread --- apps/openmw/mwrender/renderingmanager.cpp | 6 +++- apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/cellpreloader.cpp | 34 +++++++++++++++++++++-- apps/openmw/mwworld/cellpreloader.hpp | 6 ++-- apps/openmw/mwworld/scene.cpp | 4 +++ apps/openmw/mwworld/scene.hpp | 5 ++-- components/resource/resourcesystem.cpp | 6 +--- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d4199d5f6..07b477c43 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -233,11 +233,15 @@ namespace MWRender void RenderingManager::clearCache() { - mResourceSystem->updateCache(mViewer->getFrameStamp()->getReferenceTime()); if (mTerrain.get()) mTerrain->clearCache(); } + double RenderingManager::getReferenceTime() const + { + return mViewer->getFrameStamp()->getReferenceTime(); + } + osg::Group* RenderingManager::getLightRoot() { return mLightRoot.get(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 550e48e63..bc5db562c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -67,6 +67,8 @@ namespace MWRender void clearCache(); + double getReferenceTime() const; + osg::Group* getLightRoot(); void setNightEyeFactor(float factor); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index dd6850aba..16bf920bb 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -33,6 +34,7 @@ namespace MWWorld std::vector& mOut; }; + /// Worker thread item: preload models in a cell. class PreloadItem : public SceneUtil::WorkItem { public: @@ -92,8 +94,30 @@ namespace MWWorld std::vector > mPreloadedShapes; }; - CellPreloader::CellPreloader(Resource::SceneManager *sceneManager, Resource::BulletShapeManager* bulletShapeManager) - : mSceneManager(sceneManager) + /// Worker thread item: update the resource system's cache, effectively deleting unused entries. + class UpdateCacheItem : public SceneUtil::WorkItem + { + public: + UpdateCacheItem(Resource::ResourceSystem* resourceSystem, double referenceTime) + : mReferenceTime(referenceTime) + , mResourceSystem(resourceSystem) + { + } + + virtual void doWork() + { + osg::Timer timer; + mResourceSystem->updateCache(mReferenceTime); + std::cout << "cleared cache in " << timer.time_m() << std::endl; + } + + private: + double mReferenceTime; + Resource::ResourceSystem* mResourceSystem; + }; + + CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager) + : mResourceSystem(resourceSystem) , mBulletShapeManager(bulletShapeManager) { mWorkQueue = new SceneUtil::WorkQueue; @@ -120,7 +144,7 @@ namespace MWWorld return; } - osg::ref_ptr item (new PreloadItem(cell, mSceneManager, mBulletShapeManager)); + osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager)); mWorkQueue->addWorkItem(item); mPreloadCells[cell] = PreloadEntry(timestamp, item); @@ -129,6 +153,7 @@ namespace MWWorld void CellPreloader::updateCache(double timestamp) { // time (in seconds) to keep a preloaded cell in cache after it's no longer needed + // additionally we could support a minimum/maximum size for the cache const double expiryTime = 60.0; for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();) @@ -138,6 +163,9 @@ namespace MWWorld else ++it; } + + // the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations + mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, timestamp)); } void CellPreloader::setWorkQueue(osg::ref_ptr workQueue) diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 7e02cb7d1..b3413b174 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -7,7 +7,7 @@ namespace Resource { - class SceneManager; + class ResourceSystem; class BulletShapeManager; } @@ -18,7 +18,7 @@ namespace MWWorld class CellPreloader { public: - CellPreloader(Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager); + CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager); /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell. /// @note The cell itself must be in State_Loaded or State_Preloaded. @@ -30,7 +30,7 @@ namespace MWWorld void setWorkQueue(osg::ref_ptr workQueue); private: - Resource::SceneManager* mSceneManager; + Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; osg::ref_ptr mWorkQueue; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index f30e6efe1..70a461b5b 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -24,6 +24,7 @@ #include "class.hpp" #include "cellvisitors.hpp" #include "cellstore.hpp" +#include "cellpreloader.hpp" namespace { @@ -402,6 +403,7 @@ namespace MWWorld mCellChanged = true; + mPreloader->updateCache(mRendering.getReferenceTime()); mRendering.clearCache(); } @@ -439,6 +441,7 @@ namespace MWWorld Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) { + mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); } Scene::~Scene() @@ -515,6 +518,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + mPreloader->updateCache(mRendering.getReferenceTime()); mRendering.clearCache(); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index c6de3ebdf..b428d0ddd 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -1,12 +1,11 @@ #ifndef GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H -//#include "../mwrender/renderingmanager.hpp" - #include "ptr.hpp" #include "globals.hpp" #include +#include namespace osg { @@ -43,6 +42,7 @@ namespace MWWorld { class Player; class CellStore; + class CellPreloader; class Scene { @@ -57,6 +57,7 @@ namespace MWWorld bool mCellChanged; MWPhysics::PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; + std::auto_ptr mPreloader; void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 68ed13644..1f25bd461 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -54,13 +54,9 @@ namespace Resource void ResourceSystem::updateCache(double referenceTime) { - // TODO: call updateCache from the worker thread so the main thread isn't held up by the delete operations - // change ObjectCache to not hold lock while the unref happens - - osg::Timer timer; + // TODO: change ObjectCache to not hold lock while the unref happens 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) From c155680d3c8c22ac8dc22ccc540584e3b720990f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 00:43:37 +0100 Subject: [PATCH 23/66] Customize ObjectCache for more efficient locking in removeExpiredObjectsInCache --- components/resource/objectcache.cpp | 37 ++++++++++++++----------- components/resource/objectcache.hpp | 24 ++++++---------- components/resource/resourcemanager.cpp | 2 +- components/resource/resourcemanager.hpp | 8 ++---- components/resource/resourcesystem.cpp | 1 - 5 files changed, 32 insertions(+), 40 deletions(-) diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp index 7264d05aa..0c6066417 100644 --- a/components/resource/objectcache.cpp +++ b/components/resource/objectcache.cpp @@ -11,13 +11,10 @@ * OpenSceneGraph Public License for more details. */ -#include - -#if OSG_VERSION_LESS_THAN(3,3,3) - #include "objectcache.hpp" -using namespace osgDB; +namespace Resource +{ //////////////////////////////////////////////////////////////////////////////////////////// // @@ -95,21 +92,29 @@ void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double r void ObjectCache::removeExpiredObjectsInCache(double expiryTime) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::vector > objectsToRemove; - // Remove expired entries from object cache - ObjectCacheMap::iterator oitr = _objectCache.begin(); - while(oitr != _objectCache.end()) { - if (oitr->second.second<=expiryTime) - { - _objectCache.erase(oitr++); - } - else + OpenThreads::ScopedLock lock(_objectCacheMutex); + + // Remove expired entries from object cache + ObjectCacheMap::iterator oitr = _objectCache.begin(); + while(oitr != _objectCache.end()) { - ++oitr; + if (oitr->second.second<=expiryTime) + { + objectsToRemove.push_back(oitr->second.first); + _objectCache.erase(oitr++); + } + else + { + ++oitr; + } } } + + // note, actual unref happens outside of the lock + objectsToRemove.clear(); } void ObjectCache::removeFromObjectCache(const std::string& fileName) @@ -138,4 +143,4 @@ void ObjectCache::releaseGLObjects(osg::State* state) } } -#endif +} diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index cae48ca6b..624c38bad 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -1,3 +1,6 @@ +// Resource ObjectCache for OpenMW, forked from osgDB ObjectCache by Robert Osfield, see copyright notice below. +// The main change from the upstream version is that removeExpiredObjectsInCache no longer keeps a lock while the unref happens. + /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under @@ -11,17 +14,8 @@ * OpenSceneGraph Public License for more details. */ -// Wrapper for osgDB/ObjectCache. Works around ObjectCache not being available in old OSG 3.2. -// Use "#include objectcache.hpp" in place of "#include - -#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) -#include -#else +#ifndef OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE +#define OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE #include @@ -30,9 +24,9 @@ #include -namespace osgDB { +namespace Resource { -class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced +class ObjectCache : public osg::Referenced { public: @@ -70,7 +64,7 @@ class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced /** Get an ref_ptr from the object cache*/ osg::ref_ptr getRefFromObjectCache(const std::string& fileName); - /** call rleaseGLObjects on all objects attached to the object cache.*/ + /** call releaseGLObjects on all objects attached to the object cache.*/ void releaseGLObjects(osg::State* state); protected: @@ -88,5 +82,3 @@ class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced } #endif - -#endif diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp index 60233baa0..03e6263b9 100644 --- a/components/resource/resourcemanager.cpp +++ b/components/resource/resourcemanager.cpp @@ -7,7 +7,7 @@ namespace Resource ResourceManager::ResourceManager(const VFS::Manager *vfs, const double expiryDelay) : mVFS(vfs) - , mCache(new osgDB::ObjectCache) + , mCache(new Resource::ObjectCache) , mExpiryDelay(expiryDelay) { diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp index e45a2e9cb..a9b6ab3d5 100644 --- a/components/resource/resourcemanager.hpp +++ b/components/resource/resourcemanager.hpp @@ -8,13 +8,9 @@ namespace VFS class Manager; } -namespace osgDB -{ - class ObjectCache; -} - namespace Resource { + class ObjectCache; /// @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. @@ -34,7 +30,7 @@ namespace Resource protected: const VFS::Manager* mVFS; - osg::ref_ptr mCache; + osg::ref_ptr mCache; double mExpiryDelay; }; diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 1f25bd461..a8ab6556d 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -54,7 +54,6 @@ namespace Resource void ResourceSystem::updateCache(double referenceTime) { - // TODO: change ObjectCache to not hold lock while the unref happens for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->updateCache(referenceTime); } From 023c87b21595353b98edce6176b38f8dca52f288 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 05:13:46 -0800 Subject: [PATCH 24/66] Preload cell when the player goes near a teleport door. It works! --- apps/openmw/mwworld/cellpreloader.cpp | 2 + apps/openmw/mwworld/scene.cpp | 58 +++++++++++++++++++++++++++ apps/openmw/mwworld/scene.hpp | 3 ++ 3 files changed, 63 insertions(+) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 16bf920bb..f1366a8b2 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -66,6 +66,7 @@ namespace MWWorld /// Preload work to be called from the worker thread. virtual void doWork() { + osg::Timer preloadTimer; for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it) { try @@ -81,6 +82,7 @@ namespace MWWorld // error will be shown when visiting the cell } } + std::cout << "preloaded " << mPreloadedNodes.size() << " nodes in " << preloadTimer.time_m() << std::endl; } private: diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 70a461b5b..912a39e7c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -193,6 +193,13 @@ namespace MWWorld void Scene::update (float duration, bool paused) { + mPreloadTimer += duration; + if (mPreloadTimer > 1.f) + { + preloadCells(); + mPreloadTimer = 0.f; + } + mRendering.update (duration, paused); } @@ -440,6 +447,7 @@ namespace MWWorld Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) + , mPreloadTimer(0.f) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); } @@ -603,4 +611,54 @@ namespace MWWorld return Ptr(); } + + void Scene::preloadCells() + { + std::vector teleportDoors; + for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); + iter!=mActiveCells.end(); ++iter) + { + const MWWorld::CellStore* cellStore = *iter; + typedef MWWorld::CellRefList::List DoorList; + const DoorList &doors = cellStore->getReadOnlyDoors().mList; + for (DoorList::const_iterator doorIt = doors.begin(); doorIt != doors.end(); ++doorIt) + { + if (!doorIt->mRef.getTeleport()) { + continue; + } + teleportDoors.push_back(MWWorld::ConstPtr(&*doorIt, cellStore)); + } + } + + const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const float preloadDist = 500.f; + + for (std::vector::iterator it = teleportDoors.begin(); it != teleportDoors.end(); ++it) + { + const MWWorld::ConstPtr& door = *it; + float sqrDistToPlayer = (player.getRefData().getPosition().asVec3() - door.getRefData().getPosition().asVec3()).length2(); + + if (sqrDistToPlayer < preloadDist*preloadDist) + { + MWWorld::CellStore* targetCell = NULL; + try + { + if (!door.getCellRef().getDestCell().empty()) + targetCell = MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell()); + else + { + int x,y; + MWBase::Environment::get().getWorld()->positionToIndex (door.getCellRef().getDoorDest().pos[0], door.getCellRef().getDoorDest().pos[1], x, y); + targetCell = MWBase::Environment::get().getWorld()->getExterior(x, y); + } + } + catch (std::exception& e) + { + // ignore error for now, would spam the log too much + } + if (targetCell) + mPreloader->preload(targetCell, mRendering.getReferenceTime()); + } + } + } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b428d0ddd..4ac0d0666 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -58,6 +58,7 @@ namespace MWWorld MWPhysics::PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; std::auto_ptr mPreloader; + float mPreloadTimer; void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); @@ -112,6 +113,8 @@ namespace MWWorld bool isCellActive(const CellStore &cell); Ptr searchPtrViaActorId (int actorId); + + void preloadCells(); }; } From 8592166eeb5acf5bbffb16125b0ccf5f98dc2b0b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 05:27:19 -0800 Subject: [PATCH 25/66] Preload surrounding cells when preloading an exterior cell destination --- apps/openmw/mwworld/cellpreloader.cpp | 2 ++ apps/openmw/mwworld/scene.cpp | 29 +++++++++++++++------------ apps/openmw/mwworld/scene.hpp | 1 + 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index f1366a8b2..429e3294d 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -66,6 +66,8 @@ namespace MWWorld /// Preload work to be called from the worker thread. virtual void doWork() { + // TODO: make CellStore::loadRefs thread safe so we can call it from here + osg::Timer preloadTimer; for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it) { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 912a39e7c..38b762018 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -334,15 +334,13 @@ namespace MWWorld std::string loadingExteriorText = "#{sLoadingMessage3}"; loadingListener->setLabel(loadingExteriorText); - const int halfGridSize = Settings::Manager::getInt("exterior cell load distance", "Cells"); - CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) { if ((*active)->getCell()->isExterior()) { - if (std::abs (X-(*active)->getCell()->getGridX())<=halfGridSize && - std::abs (Y-(*active)->getCell()->getGridY())<=halfGridSize) + if (std::abs (X-(*active)->getCell()->getGridX())<=mHalfGridSize && + std::abs (Y-(*active)->getCell()->getGridY())<=mHalfGridSize) { // keep cells within the new grid ++active; @@ -354,9 +352,9 @@ namespace MWWorld int refsToLoad = 0; // get the number of refs to load - for (int x=X-halfGridSize; x<=X+halfGridSize; ++x) + for (int x=X-mHalfGridSize; x<=X+mHalfGridSize; ++x) { - for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y) + for (int y=Y-mHalfGridSize; y<=Y+mHalfGridSize; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); @@ -379,9 +377,9 @@ namespace MWWorld loadingListener->setProgressRange(refsToLoad); // Load cells - for (int x=X-halfGridSize; x<=X+halfGridSize; ++x) + for (int x=X-mHalfGridSize; x<=X+mHalfGridSize; ++x) { - for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y) + for (int y=Y-mHalfGridSize; y<=Y+mHalfGridSize; ++y) { CellStoreCollection::iterator iter = mActiveCells.begin(); @@ -448,6 +446,7 @@ namespace MWWorld Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) , mPreloadTimer(0.f) + , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); } @@ -640,24 +639,28 @@ namespace MWWorld if (sqrDistToPlayer < preloadDist*preloadDist) { - MWWorld::CellStore* targetCell = NULL; try { if (!door.getCellRef().getDestCell().empty()) - targetCell = MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell()); + mPreloader->preload(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell()), mRendering.getReferenceTime()); else { int x,y; MWBase::Environment::get().getWorld()->positionToIndex (door.getCellRef().getDoorDest().pos[0], door.getCellRef().getDoorDest().pos[1], x, y); - targetCell = MWBase::Environment::get().getWorld()->getExterior(x, y); + + for (int dx = -mHalfGridSize; dx <= mHalfGridSize; ++dx) + { + for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy) + { + mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(x+dx, y+dy), mRendering.getReferenceTime()); + } + } } } catch (std::exception& e) { // ignore error for now, would spam the log too much } - if (targetCell) - mPreloader->preload(targetCell, mRendering.getReferenceTime()); } } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 4ac0d0666..68748af85 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -59,6 +59,7 @@ namespace MWWorld MWRender::RenderingManager& mRendering; std::auto_ptr mPreloader; float mPreloadTimer; + int mHalfGridSize; void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); From 8b981ab507bf7e940c9ed86f466c585f9258ffeb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 05:53:42 -0800 Subject: [PATCH 26/66] Crash fix --- components/resource/scenemanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 4ed14241a..c2be3960f 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -200,7 +200,8 @@ namespace Resource for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); - apply(texture); + if (texture) + apply(texture); } } From c3ad4dad7544bd2e0ea1f7931ce8854d87e5d535 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 05:53:56 -0800 Subject: [PATCH 27/66] Fix applying of filter settings on terrain textures --- components/terrain/terraingrid.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index eb085ca42..4f7a0772b 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -139,15 +139,16 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu std::vector > layerTextures; for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { - osg::ref_ptr tex = mTextureCache[it->mDiffuseMap]; - if (!tex) + osg::ref_ptr texture = mTextureCache[it->mDiffuseMap]; + if (!texture) { - tex = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap)); - tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); - tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); - mTextureCache[it->mDiffuseMap] = tex; + texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap)); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + mResourceSystem->getSceneManager()->applyFilterSettings(texture); + mTextureCache[it->mDiffuseMap] = texture; } - layerTextures.push_back(tex); + layerTextures.push_back(texture); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back()); } @@ -161,7 +162,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu texture->setResizeNonPowerOfTwoHint(false); texture->getOrCreateUserDataContainer()->addDescription("dont_override_filter"); blendmapTextures.push_back(texture); - mResourceSystem->getSceneManager()->applyFilterSettings(texture); textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back()); } From 49ecac4ced6a562f6b0e6491e87ab6d3864f61c9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 07:37:35 -0800 Subject: [PATCH 28/66] Add a mutex lock around the SharedStateManager --- components/resource/scenemanager.cpp | 2 ++ components/resource/scenemanager.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index c2be3960f..eecee813f 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -357,7 +357,9 @@ namespace Resource loaded->accept(setFilterSettingsControllerVisitor); // share state + mSharedStateMutex.lock(); osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get()); + mSharedStateMutex.unlock(); if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 173131e66..0345fff22 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -89,6 +89,8 @@ namespace Resource void setUnRefImageDataAfterApply(bool unref); private: + OpenThreads::Mutex mSharedStateMutex; + Resource::ImageManager* mImageManager; Resource::NifFileManager* mNifFileManager; From 610257cd3a396c8e4fe557584a89395371af88b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 07:37:56 -0800 Subject: [PATCH 29/66] Preload the exterior cell grid --- apps/openmw/mwworld/scene.cpp | 53 +++++++++++++++++++++++++++++++---- apps/openmw/mwworld/scene.hpp | 8 ++++-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 38b762018..b859efbf1 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -194,7 +194,7 @@ namespace MWWorld void Scene::update (float duration, bool paused) { mPreloadTimer += duration; - if (mPreloadTimer > 1.f) + if (mPreloadTimer > 0.5f) { preloadCells(); mPreloadTimer = 0.f; @@ -313,7 +313,7 @@ namespace MWWorld getGridCenter(cellX, cellY); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = 8192/2 + 1024; // 1/2 cell size + threshold + const float maxDistance = 8192/2 + mCellLoadingThreshold; // 1/2 cell size + threshold float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); if (distance > maxDistance) { @@ -447,6 +447,8 @@ namespace MWWorld : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) , mPreloadTimer(0.f) , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) + , mCellLoadingThreshold(1024.f) + , mPreloadDistance(1000.f) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); } @@ -612,6 +614,12 @@ namespace MWWorld } void Scene::preloadCells() + { + preloadTeleportDoorDestinations(); + preloadExteriorGrid(); + } + + void Scene::preloadTeleportDoorDestinations() { std::vector teleportDoors; for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); @@ -630,14 +638,12 @@ namespace MWWorld } const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - const float preloadDist = 500.f; - for (std::vector::iterator it = teleportDoors.begin(); it != teleportDoors.end(); ++it) { const MWWorld::ConstPtr& door = *it; float sqrDistToPlayer = (player.getRefData().getPosition().asVec3() - door.getRefData().getPosition().asVec3()).length2(); - if (sqrDistToPlayer < preloadDist*preloadDist) + if (sqrDistToPlayer < mPreloadDistance*mPreloadDistance) { try { @@ -664,4 +670,41 @@ namespace MWWorld } } } + + void Scene::preloadExteriorGrid() + { + if (!MWBase::Environment::get().getWorld()->isCellExterior()) + return; + + int halfGridSizePlusOne = mHalfGridSize + 1; + + const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); + + int cellX,cellY; + getGridCenter(cellX,cellY); + + float centerX, centerY; + MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); + + for (int dx = -halfGridSizePlusOne; dx <= halfGridSizePlusOne; ++dx) + { + for (int dy = -halfGridSizePlusOne; dy <= halfGridSizePlusOne; ++dy) + { + if (dy != halfGridSizePlusOne && dy != -halfGridSizePlusOne && dx != halfGridSizePlusOne && dx != -halfGridSizePlusOne) + continue; // only care about the outer (not yet loaded) part of the grid + + float thisCellCenterX, thisCellCenterY; + MWBase::Environment::get().getWorld()->indexToPosition(cellX+dx, cellY+dy, thisCellCenterX, thisCellCenterY, true); + + float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); + float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; + + std::cout << cellX+dx << " " << cellY+dy << " dist " << dist << " need " << loadDist << std::endl; + + if (dist < loadDist) + mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy), mRendering.getReferenceTime()); + } + } + } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 68748af85..36021819e 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -60,6 +60,8 @@ namespace MWWorld std::auto_ptr mPreloader; float mPreloadTimer; int mHalfGridSize; + float mCellLoadingThreshold; + float mPreloadDistance; void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); @@ -68,6 +70,10 @@ namespace MWWorld void getGridCenter(int& cellX, int& cellY); + void preloadCells(); + void preloadTeleportDoorDestinations(); + void preloadExteriorGrid(); + public: Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics); @@ -114,8 +120,6 @@ namespace MWWorld bool isCellActive(const CellStore &cell); Ptr searchPtrViaActorId (int actorId); - - void preloadCells(); }; } From 5efaa9817c6172f5be24a0efdff7f308db4e71f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 18:01:14 +0100 Subject: [PATCH 30/66] Add preloading settings --- apps/openmw/mwworld/scene.cpp | 16 +++++++++------- apps/openmw/mwworld/scene.hpp | 1 + files/settings-default.cfg | 6 ++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b859efbf1..c788df234 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -193,11 +193,14 @@ namespace MWWorld void Scene::update (float duration, bool paused) { - mPreloadTimer += duration; - if (mPreloadTimer > 0.5f) + if (mPreloadEnabled) { - preloadCells(); - mPreloadTimer = 0.f; + mPreloadTimer += duration; + if (mPreloadTimer > 0.5f) + { + preloadCells(); + mPreloadTimer = 0.f; + } } mRendering.update (duration, paused); @@ -448,7 +451,8 @@ namespace MWWorld , mPreloadTimer(0.f) , mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells")) , mCellLoadingThreshold(1024.f) - , mPreloadDistance(1000.f) + , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) + , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); } @@ -700,8 +704,6 @@ namespace MWWorld float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; - std::cout << cellX+dx << " " << cellY+dy << " dist " << dist << " need " << loadDist << std::endl; - if (dist < loadDist) mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy), mRendering.getReferenceTime()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 36021819e..34e137e45 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -62,6 +62,7 @@ namespace MWWorld int mHalfGridSize; float mCellLoadingThreshold; float mPreloadDistance; + bool mPreloadEnabled; void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index dddf1a29b..c03342600 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -37,6 +37,12 @@ first person field of view = 55.0 # dramatically affect performance, see documentation for details. exterior cell load distance = 1 +# Preload cells in a background thread +preload enabled = true + +# Preloading distance threshold +preload distance = 1000 + [Map] # Size of each exterior cell in pixels in the world map. (e.g. 12 to 24). From 778bce3ae939ec17345b09cb1365215079ee9640 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 18:27:12 +0100 Subject: [PATCH 31/66] Remove unused ObjectCache functions --- components/resource/objectcache.cpp | 24 ------------------------ components/resource/objectcache.hpp | 6 ------ 2 files changed, 30 deletions(-) diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp index 0c6066417..54576a122 100644 --- a/components/resource/objectcache.cpp +++ b/components/resource/objectcache.cpp @@ -31,42 +31,18 @@ ObjectCache::~ObjectCache() // OSG_NOTICE<<"Destructed ObjectCache"< lock1(_objectCacheMutex); - OpenThreads::ScopedLock lock2(objectCache->_objectCacheMutex); - - // OSG_NOTICE<<"Inserting objects to main ObjectCache "<_objectCache.size()<_objectCache.begin(), objectCache->_objectCache.end()); -} - - void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp) { OpenThreads::ScopedLock lock(_objectCacheMutex); _objectCache[filename]=ObjectTimeStampPair(object,timestamp); } -osg::Object* ObjectCache::getFromObjectCache(const std::string& fileName) -{ - OpenThreads::ScopedLock lock(_objectCacheMutex); - ObjectCacheMap::iterator itr = _objectCache.find(fileName); - if (itr!=_objectCache.end()) return itr->second.first.get(); - else return 0; -} - osg::ref_ptr ObjectCache::getRefFromObjectCache(const std::string& fileName) { OpenThreads::ScopedLock lock(_objectCacheMutex); ObjectCacheMap::iterator itr = _objectCache.find(fileName); if (itr!=_objectCache.end()) { - // OSG_NOTICE<<"Found "<second.first; } else return 0; diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index 624c38bad..b933933f3 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -49,18 +49,12 @@ class ObjectCache : public osg::Referenced /** Remove all objects in the cache regardless of having external references or expiry times.*/ void clear(); - /** Add contents of specified ObjectCache to this object cache.*/ - void addObjectCache(ObjectCache* object); - /** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/ void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0); /** Remove Object from cache.*/ void removeFromObjectCache(const std::string& fileName); - /** Get an Object from the object cache*/ - osg::Object* getFromObjectCache(const std::string& fileName); - /** Get an ref_ptr from the object cache*/ osg::ref_ptr getRefFromObjectCache(const std::string& fileName); From 41233fc8e5717ba0d37dd5c3afb513a7e5702c62 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 18:56:21 +0100 Subject: [PATCH 32/66] Keep a reference to the original scene template for as long as the instance is used --- components/resource/objectcache.cpp | 2 -- components/resource/scenemanager.cpp | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp index 54576a122..17368f34f 100644 --- a/components/resource/objectcache.cpp +++ b/components/resource/objectcache.cpp @@ -23,12 +23,10 @@ namespace Resource ObjectCache::ObjectCache(): osg::Referenced(true) { -// OSG_NOTICE<<"Constructed ObjectCache"< scene = getTemplate(name); osg::ref_ptr cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); + + // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache + cloned->getOrCreateUserDataContainer()->addUserObject(const_cast(scene.get())); + return cloned; } From a81b10b4153751ee7c1cc117f8b9096dda70bb8b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 19:05:55 +0100 Subject: [PATCH 33/66] Make the cache expiryDelay configurable --- apps/openmw/mwworld/cellpreloader.cpp | 12 ++++++++---- apps/openmw/mwworld/cellpreloader.hpp | 4 ++++ apps/openmw/mwworld/scene.cpp | 4 ++++ components/resource/niffilemanager.cpp | 3 +-- components/resource/resourcemanager.cpp | 9 +++++++-- components/resource/resourcemanager.hpp | 6 ++++-- components/resource/resourcesystem.cpp | 10 ++++++++++ components/resource/resourcesystem.hpp | 3 +++ files/settings-default.cfg | 4 ++++ 9 files changed, 45 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 429e3294d..13ce75d68 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -123,6 +123,7 @@ namespace MWWorld CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager) : mResourceSystem(resourceSystem) , mBulletShapeManager(bulletShapeManager) + , mExpiryDelay(0.0) { mWorkQueue = new SceneUtil::WorkQueue; } @@ -156,13 +157,11 @@ namespace MWWorld void CellPreloader::updateCache(double timestamp) { - // time (in seconds) to keep a preloaded cell in cache after it's no longer needed - // additionally we could support a minimum/maximum size for the cache - const double expiryTime = 60.0; + // TODO: add settings for a minimum/maximum size of the cache for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();) { - if (it->second.mTimeStamp < timestamp - expiryTime) + if (it->second.mTimeStamp < timestamp - mExpiryDelay) mPreloadCells.erase(it++); else ++it; @@ -172,6 +171,11 @@ namespace MWWorld mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, timestamp)); } + void CellPreloader::setExpiryDelay(double expiryDelay) + { + mExpiryDelay = expiryDelay; + } + void CellPreloader::setWorkQueue(osg::ref_ptr workQueue) { mWorkQueue = workQueue; diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index b3413b174..5a2432179 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -27,12 +27,16 @@ namespace MWWorld /// Removes preloaded cells that have not had a preload request for a while. void updateCache(double timestamp); + /// How long to keep a preloaded cell in cache after it's no longer requested. + void setExpiryDelay(double expiryDelay); + void setWorkQueue(osg::ref_ptr workQueue); private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; osg::ref_ptr mWorkQueue; + double mExpiryDelay; struct PreloadEntry { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c788df234..86f40df73 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -455,6 +455,10 @@ namespace MWWorld , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); + + float cacheExpiryDelay = Settings::Manager::getFloat("cache expiry delay", "Cells"); + rendering.getResourceSystem()->setExpiryDelay(cacheExpiryDelay); + mPreloader->setExpiryDelay(cacheExpiryDelay); } Scene::~Scene() diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp index 3c7437520..ecb63db60 100644 --- a/components/resource/niffilemanager.cpp +++ b/components/resource/niffilemanager.cpp @@ -29,8 +29,7 @@ namespace Resource }; NifFileManager::NifFileManager(const VFS::Manager *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. + : ResourceManager(vfs) { } diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp index 03e6263b9..b6b38bfa2 100644 --- a/components/resource/resourcemanager.cpp +++ b/components/resource/resourcemanager.cpp @@ -5,10 +5,10 @@ namespace Resource { - ResourceManager::ResourceManager(const VFS::Manager *vfs, const double expiryDelay) + ResourceManager::ResourceManager(const VFS::Manager *vfs) : mVFS(vfs) , mCache(new Resource::ObjectCache) - , mExpiryDelay(expiryDelay) + , mExpiryDelay(0.0) { } @@ -26,6 +26,11 @@ namespace Resource mCache->clear(); } + void ResourceManager::setExpiryDelay(double expiryDelay) + { + mExpiryDelay = expiryDelay; + } + const VFS::Manager* ResourceManager::getVFS() const { return mVFS; diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp index a9b6ab3d5..d2a1d1023 100644 --- a/components/resource/resourcemanager.hpp +++ b/components/resource/resourcemanager.hpp @@ -17,8 +17,7 @@ namespace Resource 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); + ResourceManager(const VFS::Manager* vfs); /// Clear cache entries that have not been referenced for longer than expiryDelay. virtual void updateCache(double referenceTime); @@ -26,6 +25,9 @@ namespace Resource /// Clear all cache entries regardless of having external references. virtual void clearCache(); + /// How long to keep objects in cache after no longer being referenced. + void setExpiryDelay (double expiryDelay); + const VFS::Manager* getVFS() const; protected: diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index a8ab6556d..5772f2b85 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -52,6 +52,16 @@ namespace Resource return mKeyframeManager.get(); } + void ResourceSystem::setExpiryDelay(double expiryDelay) + { + for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + (*it)->setExpiryDelay(expiryDelay); + + // NIF files aren't needed any more once the converted objects are cached in SceneManager / BulletShapeManager, + // so no point in using an expiry delay + mNifFileManager->setExpiryDelay(0.0); + } + void ResourceSystem::updateCache(double referenceTime) { for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 30607432d..9b933ffc4 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -43,6 +43,9 @@ namespace Resource /// @note Does not delete resourceMgr. void removeResourceManager(ResourceManager* resourceMgr); + /// How long to keep objects in cache after no longer being referenced. + void setExpiryDelay(double expiryDelay); + /// @note May be called from any thread. const VFS::Manager* getVFS() const; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c03342600..4df20e709 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -43,6 +43,10 @@ preload enabled = true # Preloading distance threshold preload distance = 1000 +# How long to keep preloaded cells and cached models/textures/collision shapes in cache +# after they're no longer referenced/required (in seconds) +cache expiry delay = 300 + [Map] # Size of each exterior cell in pixels in the world map. (e.g. 12 to 24). From c8054424c946ed5433b325d2f1301f16d7d78b64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 7 Feb 2016 22:37:52 +0100 Subject: [PATCH 34/66] Preload items equipped by NPCs --- apps/openmw/mwworld/cellpreloader.cpp | 68 ++++++++++++++++++++++++--- apps/openmw/mwworld/cellpreloader.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 7 +++ components/resource/scenemanager.cpp | 3 ++ 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 13ce75d68..6bf28cb48 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -5,10 +5,14 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/esmstore.hpp" + #include "cellstore.hpp" #include "manualref.hpp" #include "class.hpp" @@ -23,11 +27,54 @@ namespace MWWorld { } - virtual bool operator()(const MWWorld::ConstPtr& ptr) + virtual bool operator()(const MWWorld::Ptr& ptr) { std::string model = ptr.getClass().getModel(ptr); if (!model.empty()) mOut.push_back(model); + + // TODO: preload NPC body parts (mHead / mHair) + + // FIXME: use const version of InventoryStore functions once they are available + if (ptr.getClass().hasInventoryStore(ptr)) + { + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator equipped = invStore.getSlot(slot); + if (equipped != invStore.end()) + { + std::vector parts; + if(equipped->getTypeName() == typeid(ESM::Clothing).name()) + { + const ESM::Clothing *clothes = equipped->get()->mBase; + parts = clothes->mParts.mParts; + } + else if(equipped->getTypeName() == typeid(ESM::Armor).name()) + { + const ESM::Armor *armor = equipped->get()->mBase; + parts = armor->mParts.mParts; + } + else + { + model = equipped->getClass().getModel(*equipped); + if (!model.empty()) + mOut.push_back(model); + } + + for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) + { + const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(it->mMale); + if (part && !part->mModel.empty()) + mOut.push_back("meshes/"+part->mModel); + part = MWBase::Environment::get().getWorld()->getStore().get().search(it->mFemale); + if (part && !part->mModel.empty()) + mOut.push_back("meshes/"+part->mModel); + } + } + } + } + return true; } @@ -39,14 +86,15 @@ namespace MWWorld { public: /// Constructor to be called from the main thread. - PreloadItem(const MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager) + PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager) : mSceneManager(sceneManager) , mBulletShapeManager(bulletShapeManager) { + osg::Timer timer; + ListModelsVisitor visitor (mMeshes); if (cell->getState() == MWWorld::CellStore::State_Loaded) { - ListModelsVisitor visitor (mMeshes); - cell->forEachConst(visitor); + cell->forEach(visitor); } else { @@ -61,6 +109,7 @@ namespace MWWorld mMeshes.push_back(model); } } + std::cout << "listed models in " << timer.time_m() << std::endl; } /// Preload work to be called from the worker thread. @@ -73,9 +122,16 @@ namespace MWWorld { try { + std::string mesh = *it; + Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); + + //std::cout << "preloading " << mesh << std::endl; + mPreloadedNodes.push_back(mSceneManager->getTemplate(*it)); mPreloadedShapes.push_back(mBulletShapeManager->getShape(*it)); + // TODO: load .kf + // 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) @@ -84,7 +140,7 @@ namespace MWWorld // error will be shown when visiting the cell } } - std::cout << "preloaded " << mPreloadedNodes.size() << " nodes in " << preloadTimer.time_m() << std::endl; + //std::cout << "preloaded " << mPreloadedNodes.size() << " nodes in " << preloadTimer.time_m() << std::endl; } private: @@ -128,7 +184,7 @@ namespace MWWorld mWorkQueue = new SceneUtil::WorkQueue; } - void CellPreloader::preload(const CellStore *cell, double timestamp) + void CellPreloader::preload(CellStore *cell, double timestamp) { if (!mWorkQueue) { diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 5a2432179..c7495182b 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -22,7 +22,7 @@ namespace MWWorld /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell. /// @note The cell itself must be in State_Loaded or State_Preloaded. - void preload(const MWWorld::CellStore* cell, double timestamp); + void preload(MWWorld::CellStore* cell, double timestamp); /// Removes preloaded cells that have not had a preload request for a while. void updateCache(double timestamp); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 86f40df73..321c1c622 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -329,6 +330,7 @@ namespace MWWorld void Scene::changeCellGrid (int X, int Y) { + osg::Timer timer; Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); @@ -413,6 +415,8 @@ namespace MWWorld mPreloader->updateCache(mRendering.getReferenceTime()); mRendering.clearCache(); + + std::cout << "changeCellGrid took " << timer.time_m() << std::endl; } void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) @@ -477,6 +481,7 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + osg::Timer timer; CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); bool loadcell = (mCurrentCell == NULL); if(!loadcell) @@ -537,6 +542,8 @@ namespace MWWorld mPreloader->updateCache(mRendering.getReferenceTime()); mRendering.clearCache(); + + std::cout << "change to interior took " << timer.time_m() << std::endl; } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 46d975631..f083e8175 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -1,5 +1,6 @@ #include "scenemanager.hpp" +#include #include #include #include @@ -364,6 +365,8 @@ namespace Resource if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); + //std::cout << "loading " << normalized << std::endl; + mCache->addEntryToObjectCache(normalized, loaded); return loaded; } From e4e313fe129fa2c96440e53a8fc9e2ba8bca2fd1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 14:41:21 +0100 Subject: [PATCH 35/66] Remove outdated comment --- apps/openmw/mwworld/cellpreloader.cpp | 2 +- components/resource/resourcemanager.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 6bf28cb48..d9875e0b7 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -140,7 +140,7 @@ namespace MWWorld // error will be shown when visiting the cell } } - //std::cout << "preloaded " << mPreloadedNodes.size() << " nodes in " << preloadTimer.time_m() << std::endl; + std::cout << "preloaded " << mPreloadedNodes.size() << " nodes in " << preloadTimer.time_m() << std::endl; } private: diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp index b6b38bfa2..e5f6d9f43 100644 --- a/components/resource/resourcemanager.cpp +++ b/components/resource/resourcemanager.cpp @@ -15,8 +15,6 @@ namespace Resource 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); } From ef5de945483ab719df31cb3c392a2a0e8b46c9dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 15:31:09 +0100 Subject: [PATCH 36/66] Fix correctActorModelPath --- apps/openmw/mwworld/cellpreloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index d9875e0b7..b45d1de5b 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -123,7 +123,7 @@ namespace MWWorld try { std::string mesh = *it; - Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); + mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); //std::cout << "preloading " << mesh << std::endl; From fc0be77e4c02ee182141c103be3404757240708b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 15:51:31 +0100 Subject: [PATCH 37/66] Preload keyframes --- apps/openmw/mwworld/cellpreloader.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index b45d1de5b..4780582c5 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -86,9 +88,10 @@ namespace MWWorld { public: /// Constructor to be called from the main thread. - PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager) + PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager) : mSceneManager(sceneManager) , mBulletShapeManager(bulletShapeManager) + , mKeyframeManager(keyframeManager) { osg::Timer timer; ListModelsVisitor visitor (mMeshes); @@ -130,7 +133,21 @@ namespace MWWorld mPreloadedNodes.push_back(mSceneManager->getTemplate(*it)); mPreloadedShapes.push_back(mBulletShapeManager->getShape(*it)); - // TODO: load .kf + size_t slashpos = mesh.find_last_of("/\\"); + if (slashpos != std::string::npos && slashpos != mesh.size()-1) + { + Misc::StringUtils::lowerCaseInPlace(mesh); + if (mesh[slashpos+1] == 'x') + { + std::string kfname = mesh; + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + { + kfname.replace(kfname.size()-4, 4, ".kf"); + mPreloadedKeyframes.push_back(mKeyframeManager->get(kfname)); + } + + } + } // TODO: do a createInstance() and hold on to it since we can make use of it when the cell goes active } @@ -148,10 +165,12 @@ namespace MWWorld MeshList mMeshes; Resource::SceneManager* mSceneManager; Resource::BulletShapeManager* mBulletShapeManager; + Resource::KeyframeManager* mKeyframeManager; // keep a ref to the loaded object to make sure it stays loaded as long as this cell is in the preloaded state std::vector > mPreloadedNodes; std::vector > mPreloadedShapes; + std::vector > mPreloadedKeyframes; }; /// Worker thread item: update the resource system's cache, effectively deleting unused entries. @@ -205,7 +224,7 @@ namespace MWWorld return; } - osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager)); + osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager())); mWorkQueue->addWorkItem(item); mPreloadCells[cell] = PreloadEntry(timestamp, item); From b2019d31c72d5dd4a30c9b8b7aa214d6dbdf24da Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 15:55:05 +0100 Subject: [PATCH 38/66] Mark thread safe methods in BsaFile --- components/bsa/bsa_file.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 8ed63f35d..5ff86ef65 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -90,6 +90,7 @@ private: void readHeader(); /// Get the index of a given file name, or -1 if not found + /// @note Thread safe. int getIndex(const char *str) const; public: @@ -116,12 +117,17 @@ public: /** Open a file contained in the archive. Throws an exception if the file doesn't exist. + * @note Thread safe. */ Files::IStreamPtr getFile(const char *file); + /** Open a file contained in the archive. + * @note Thread safe. + */ Files::IStreamPtr getFile(const FileStruct* file); /// Get a list of all files + /// @note Thread safe. const FileList &getList() const { return files; } }; From 84f01b7527c5359ba441e0b3efc1f15c469f0d0c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 16:27:28 +0100 Subject: [PATCH 39/66] Remove unneeded forward declaration --- components/sceneutil/workqueue.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index 06489dfbb..bc2e55647 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -37,7 +37,6 @@ namespace SceneUtil OpenThreads::Condition mCondition; }; - class WorkQueue; class WorkThread; /// @brief A work queue that users can push work items onto, to be completed by one or more background threads. From effe022bb2a2e5d15cc03324facd11a145d639dd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 20:52:32 +0100 Subject: [PATCH 40/66] Move preload model list to MWClass, preload NPC head/hair --- apps/openmw/mwclass/creature.cpp | 23 +++++++++ apps/openmw/mwclass/creature.hpp | 3 ++ apps/openmw/mwclass/npc.cpp | 68 +++++++++++++++++++++++++++ apps/openmw/mwclass/npc.hpp | 3 ++ apps/openmw/mwworld/cellpreloader.cpp | 46 +----------------- apps/openmw/mwworld/class.cpp | 7 +++ apps/openmw/mwworld/class.hpp | 3 ++ 7 files changed, 108 insertions(+), 45 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e9502d86b..6e9cfccb9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -182,6 +182,29 @@ namespace MWClass return ""; } + void Creature::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector &models) const + { + std::string model = getModel(ptr); + if (!model.empty()) + models.push_back(model); + + // FIXME: use const version of InventoryStore functions once they are available + if (ptr.getClass().hasInventoryStore(ptr)) + { + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator equipped = invStore.getSlot(slot); + if (equipped != invStore.end()) + { + model = equipped->getClass().getModel(*equipped); + if (!model.empty()) + models.push_back(model); + } + } + } + } + std::string Creature::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index cb89a53d6..bea56887a 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -101,6 +101,9 @@ namespace MWClass virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; + virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector& models) const; + ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). + virtual bool isActor() const { return true; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 57a6d088a..14bd0640c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -429,6 +429,74 @@ namespace MWClass return model; } + void Npc::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector &models) const + { + const MWWorld::LiveCellRef *npc = ptr.get(); + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().search(npc->mBase->mRace); + if(race && race->mData.mFlags & ESM::Race::Beast) + models.push_back("meshes\\base_animkna.nif"); + + // keep these always loaded just in case + models.push_back("meshes/xargonian_swimkna.nif"); + models.push_back("meshes/xbase_anim_female.nif"); + models.push_back("meshes/xbase_anim.nif"); + + if (!npc->mBase->mModel.empty()) + models.push_back("meshes/"+npc->mBase->mModel); + + if (!npc->mBase->mHead.empty()) + { + const ESM::BodyPart* head = MWBase::Environment::get().getWorld()->getStore().get().search(npc->mBase->mHead); + if (head) + models.push_back("meshes/"+head->mModel); + } + if (!npc->mBase->mHair.empty()) + { + const ESM::BodyPart* hair = MWBase::Environment::get().getWorld()->getStore().get().search(npc->mBase->mHair); + if (hair) + models.push_back("meshes/"+hair->mModel); + } + + // FIXME: use const version of InventoryStore functions once they are available + if (ptr.getClass().hasInventoryStore(ptr)) + { + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator equipped = invStore.getSlot(slot); + if (equipped != invStore.end()) + { + std::vector parts; + if(equipped->getTypeName() == typeid(ESM::Clothing).name()) + { + const ESM::Clothing *clothes = equipped->get()->mBase; + parts = clothes->mParts.mParts; + } + else if(equipped->getTypeName() == typeid(ESM::Armor).name()) + { + const ESM::Armor *armor = equipped->get()->mBase; + parts = armor->mParts.mParts; + } + else + { + std::string model = equipped->getClass().getModel(*equipped); + if (!model.empty()) + models.push_back(model); + } + + for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) + { + const std::string& partname = (npc->mBase->mFlags & ESM::NPC::Female) ? it->mFemale : it->mMale; + const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(partname); + if (part && !part->mModel.empty()) + models.push_back("meshes/"+part->mModel); + } + } + } + } + + } + std::string Npc::getName (const MWWorld::ConstPtr& ptr) const { if(ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 5df34380a..95edbd408 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -75,6 +75,9 @@ namespace MWClass virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector& models) const; + ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 4780582c5..1a8ee2709 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -31,51 +31,7 @@ namespace MWWorld virtual bool operator()(const MWWorld::Ptr& ptr) { - std::string model = ptr.getClass().getModel(ptr); - if (!model.empty()) - mOut.push_back(model); - - // TODO: preload NPC body parts (mHead / mHair) - - // FIXME: use const version of InventoryStore functions once they are available - if (ptr.getClass().hasInventoryStore(ptr)) - { - MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); - for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator equipped = invStore.getSlot(slot); - if (equipped != invStore.end()) - { - std::vector parts; - if(equipped->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = equipped->get()->mBase; - parts = clothes->mParts.mParts; - } - else if(equipped->getTypeName() == typeid(ESM::Armor).name()) - { - const ESM::Armor *armor = equipped->get()->mBase; - parts = armor->mParts.mParts; - } - else - { - model = equipped->getClass().getModel(*equipped); - if (!model.empty()) - mOut.push_back(model); - } - - for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) - { - const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(it->mMale); - if (part && !part->mModel.empty()) - mOut.push_back("meshes/"+part->mModel); - part = MWBase::Environment::get().getWorld()->getStore().get().search(it->mFemale); - if (part && !part->mModel.empty()) - mOut.push_back("meshes/"+part->mModel); - } - } - } - } + ptr.getClass().getModelsToPreload(ptr, mOut); return true; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 7f2d759b9..ecfe7cb7c 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -291,6 +291,13 @@ namespace MWWorld return ""; } + void Class::getModelsToPreload(const Ptr &ptr, std::vector &models) const + { + std::string model = getModel(ptr); + if (!model.empty()) + models.push_back(model); + } + std::string Class::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const { throw std::runtime_error ("class can't be enchanted"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 85cd9ff7c..0bb3edbee 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -262,6 +262,9 @@ namespace MWWorld virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; + virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector& models) const; + ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). + virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; ///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it. From 84dcf59c50b21604c18ffc62078ea0f5d8499bd5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 8 Feb 2016 22:57:34 +0100 Subject: [PATCH 41/66] Fix preloading of equipment parts that don't separate gender --- apps/openmw/mwclass/npc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 14bd0640c..5f7f91329 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -486,7 +486,9 @@ namespace MWClass for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) { - const std::string& partname = (npc->mBase->mFlags & ESM::NPC::Female) ? it->mFemale : it->mMale; + std::string partname = (npc->mBase->mFlags & ESM::NPC::Female) ? it->mFemale : it->mMale; + if (partname.empty()) + partname = (npc->mBase->mFlags & ESM::NPC::Female) ? it->mMale : it->mFemale; const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(partname); if (part && !part->mModel.empty()) models.push_back("meshes/"+part->mModel); From 1b8e82e92978c53c4e7d13427975cb49ca72fe19 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 00:26:22 +0100 Subject: [PATCH 42/66] Preload NPC body parts --- apps/openmw/mwclass/npc.cpp | 20 ++- apps/openmw/mwrender/npcanimation.cpp | 220 +++++++++++++------------- apps/openmw/mwrender/npcanimation.hpp | 5 + 3 files changed, 136 insertions(+), 109 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5f7f91329..3519f9d83 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -37,6 +37,7 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwrender/npcanimation.hpp" #include "../mwgui/tooltips.hpp" @@ -457,7 +458,10 @@ namespace MWClass models.push_back("meshes/"+hair->mModel); } + bool female = (npc->mBase->mFlags & ESM::NPC::Female); + // FIXME: use const version of InventoryStore functions once they are available + // preload equipped items if (ptr.getClass().hasInventoryStore(ptr)) { MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); @@ -486,9 +490,9 @@ namespace MWClass for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) { - std::string partname = (npc->mBase->mFlags & ESM::NPC::Female) ? it->mFemale : it->mMale; + std::string partname = female ? it->mFemale : it->mMale; if (partname.empty()) - partname = (npc->mBase->mFlags & ESM::NPC::Female) ? it->mMale : it->mFemale; + partname = female ? it->mMale : it->mFemale; const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(partname); if (part && !part->mModel.empty()) models.push_back("meshes/"+part->mModel); @@ -497,6 +501,18 @@ namespace MWClass } } + // preload body parts + if (race) + { + const std::vector& parts = MWRender::NpcAnimation::getBodyParts(Misc::StringUtils::lowerCase(race->mId), female, false, false); + for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) + { + const ESM::BodyPart* part = *it; + if (part && !part->mModel.empty()) + models.push_back("meshes/"+part->mModel); + } + } + } std::string Npc::getName (const MWWorld::ConstPtr& ptr) const diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c8d7c79c5..026b3a2cf 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -619,116 +619,10 @@ void NpcAnimation::updateParts() showWeapons(mShowWeapons); showCarriedLeft(mShowCarriedLeft); - // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination - static std::map< std::pair,std::vector > sRaceMapping; - bool isWerewolf = (mNpcType == Type_Werewolf); - int flags = (isWerewolf ? -1 : 0); - if(!mNpc->isMale()) - { - static const int Flag_Female = 1<<0; - flags |= Flag_Female; - } - if(mViewMode == VM_FirstPerson) - { - static const int Flag_FirstPerson = 1<<1; - flags |= Flag_FirstPerson; - } - std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace)); - std::pair thisCombination = std::make_pair(race, flags); - if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) - { - typedef std::multimap BodyPartMapType; - static BodyPartMapType sBodyPartMap; - if(sBodyPartMap.empty()) - { - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); - sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); - } - - std::vector &parts = sRaceMapping[thisCombination]; - parts.resize(ESM::PRT_Count, NULL); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const MWWorld::Store &partStore = store.get(); - for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) - { - if(isWerewolf) - break; - const ESM::BodyPart& bodypart = *it; - if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) - continue; - if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) - continue; - - if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) - continue; - - bool firstPerson = (bodypart.mId.size() >= 3) - && bodypart.mId[bodypart.mId.size()-3] == '1' - && bodypart.mId[bodypart.mId.size()-2] == 's' - && bodypart.mId[bodypart.mId.size()-1] == 't'; - if(firstPerson != (mViewMode == VM_FirstPerson)) - { - if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || - bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || - bodypart.mData.mPart == ESM::BodyPart::MP_Forearm || - bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm)) - { - /* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */ - BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); - while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) - { - if(!parts[bIt->second]) - parts[bIt->second] = &*it; - ++bIt; - } - } - continue; - } - if ((!mNpc->isMale()) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) - { - // Allow opposite gender's parts as fallback if parts for our gender are missing - BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); - while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) - { - if(!parts[bIt->second]) - parts[bIt->second] = &*it; - ++bIt; - } - continue; - } - - BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); - while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) - { - parts[bIt->second] = &*it; - ++bIt; - } - } - } - - const std::vector &parts = sRaceMapping[thisCombination]; + const std::vector &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf); for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) { if(mPartPriorities[part] < 1) @@ -1116,6 +1010,118 @@ void NpcAnimation::updatePtr(const MWWorld::Ptr &updated) mHeadAnimationTime->updatePtr(updated); } +// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination +typedef std::map< std::pair,std::vector > RaceMapping; +static RaceMapping sRaceMapping; + +const std::vector& NpcAnimation::getBodyParts(const std::string &race, bool female, bool firstPerson, bool werewolf) +{ + static const int Flag_FirstPerson = 1<<1; + static const int Flag_Female = 1<<0; + + int flags = (werewolf ? -1 : 0); + if(female) + flags |= Flag_Female; + if(firstPerson) + flags |= Flag_FirstPerson; + + RaceMapping::iterator found = sRaceMapping.find(std::make_pair(race, flags)); + if (found != sRaceMapping.end()) + return found->second; + else + { + std::vector& parts = sRaceMapping[std::make_pair(race, flags)]; + + typedef std::multimap BodyPartMapType; + static BodyPartMapType sBodyPartMap; + if(sBodyPartMap.empty()) + { + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); + } + + parts.resize(ESM::PRT_Count, NULL); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::Store &partStore = store.get(); + for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) + { + if(werewolf) + break; + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + + if (!Misc::StringUtils::ciEqual(bodypart.mRace, race)) + continue; + + bool partFirstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if(partFirstPerson != (firstPerson)) + { + if(firstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || + bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart.mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm)) + { + /* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */ + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + if(!parts[bIt->second]) + parts[bIt->second] = &*it; + ++bIt; + } + } + continue; + } + + if ((female) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + { + // Allow opposite gender's parts as fallback if parts for our gender are missing + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + if(!parts[bIt->second]) + parts[bIt->second] = &*it; + ++bIt; + } + continue; + } + + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + parts[bIt->second] = &*it; + ++bIt; + } + } + return parts; + } +} + void NpcAnimation::setAccurateAiming(bool enabled) { mAccurateAiming = enabled; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index c5fc62f9c..baf9c8c24 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -10,6 +10,7 @@ namespace ESM { struct NPC; + struct BodyPart; } namespace MWRender @@ -150,6 +151,10 @@ public: void setFirstPersonOffset(const osg::Vec3f& offset); virtual void updatePtr(const MWWorld::Ptr& updated); + + /// Get a list of body parts that may be used by an NPC of given race and gender. + /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain NULL body parts. + static const std::vector& getBodyParts(const std::string& raceId, bool female, bool firstperson, bool werewolf); }; } From d16450bff2b934bc9f8053b510b1501348dc99ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 00:28:27 +0100 Subject: [PATCH 43/66] Fix correctActorModelPath in preloader not being used --- apps/openmw/mwworld/cellpreloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 1a8ee2709..06216221b 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -86,8 +86,8 @@ namespace MWWorld //std::cout << "preloading " << mesh << std::endl; - mPreloadedNodes.push_back(mSceneManager->getTemplate(*it)); - mPreloadedShapes.push_back(mBulletShapeManager->getShape(*it)); + mPreloadedNodes.push_back(mSceneManager->getTemplate(mesh)); + mPreloadedShapes.push_back(mBulletShapeManager->getShape(mesh)); size_t slashpos = mesh.find_last_of("/\\"); if (slashpos != std::string::npos && slashpos != mesh.size()-1) From f9082502f8a91132fe45ab9d3f632f865aa2a29d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 01:02:40 +0100 Subject: [PATCH 44/66] Move construction of WorkQueue to RenderingManager --- apps/openmw/mwrender/renderingmanager.cpp | 7 +++++++ apps/openmw/mwrender/renderingmanager.hpp | 9 +++++++++ apps/openmw/mwworld/cellpreloader.cpp | 1 - apps/openmw/mwworld/scene.cpp | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 07b477c43..6ba30f32c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -131,6 +132,7 @@ namespace MWRender : mViewer(viewer) , mRootNode(rootNode) , mResourceSystem(resourceSystem) + , mWorkQueue(new SceneUtil::WorkQueue) , mFogDepth(0.f) , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) @@ -231,6 +233,11 @@ namespace MWRender return mResourceSystem; } + SceneUtil::WorkQueue *RenderingManager::getWorkQueue() + { + return mWorkQueue.get(); + } + void RenderingManager::clearCache() { if (mTerrain.get()) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index bc5db562c..8bc3a0a74 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -42,6 +42,11 @@ namespace Fallback class Map; } +namespace SceneUtil +{ + class WorkQueue; +} + namespace MWRender { @@ -65,6 +70,8 @@ namespace MWRender Resource::ResourceSystem* getResourceSystem(); + SceneUtil::WorkQueue* getWorkQueue(); + void clearCache(); double getReferenceTime() const; @@ -190,6 +197,8 @@ namespace MWRender osg::ref_ptr mLightRoot; Resource::ResourceSystem* mResourceSystem; + osg::ref_ptr mWorkQueue; + osg::ref_ptr mSunLight; std::auto_ptr mPathgrid; diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 06216221b..2591e8c89 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -156,7 +156,6 @@ namespace MWWorld , mBulletShapeManager(bulletShapeManager) , mExpiryDelay(0.0) { - mWorkQueue = new SceneUtil::WorkQueue; } void CellPreloader::preload(CellStore *cell, double timestamp) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 321c1c622..2856c77e8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -459,6 +459,7 @@ namespace MWWorld , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); + mPreloader->setWorkQueue(mRendering.getWorkQueue()); float cacheExpiryDelay = Settings::Manager::getFloat("cache expiry delay", "Cells"); rendering.getResourceSystem()->setExpiryDelay(cacheExpiryDelay); From 1cda2bf796fdb27e40accfa11fcc44d9c609e9df Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 01:17:02 +0100 Subject: [PATCH 45/66] Preload sky & water from the main menu --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 34 +++++++++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwrender/sky.cpp | 40 +++++++++++++++++++++++ apps/openmw/mwrender/sky.hpp | 2 ++ apps/openmw/mwrender/water.cpp | 12 +++++++ apps/openmw/mwrender/water.hpp | 3 ++ apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 10 files changed, 104 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d30d0961a..b3a58d18e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -651,6 +651,8 @@ void OMW::Engine::go() } else if (!mSkipMenu) { + mEnvironment.getWorld()->preloadCommonAssets(); + // start in main menu mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); try diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 119ce6b21..946a9a5dd 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -95,6 +95,8 @@ namespace MWBase virtual ~World() {} + virtual void preloadCommonAssets() = 0; + virtual void startNewGame (bool bypass) = 0; ///< \param bypass Bypass regular game start. diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6ba30f32c..8c23a0707 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -127,6 +127,29 @@ namespace MWRender bool mWireframe; }; + class PreloadCommonAssetsWorkItem : public SceneUtil::WorkItem + { + public: + PreloadCommonAssetsWorkItem(Resource::ResourceSystem* resourceSystem) + : mResourceSystem(resourceSystem) + { + } + + virtual void doWork() + { + for (std::vector::const_iterator it = mModels.begin(); it != mModels.end(); ++it) + mResourceSystem->getSceneManager()->getTemplate(*it); + for (std::vector::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it) + mResourceSystem->getImageManager()->getImage(*it); + } + + std::vector mModels; + std::vector mTextures; + + private: + Resource::ResourceSystem* mResourceSystem; + }; + RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, const Fallback::Map* fallback, const std::string& resourcePath) : mViewer(viewer) @@ -238,6 +261,17 @@ namespace MWRender return mWorkQueue.get(); } + void RenderingManager::preloadCommonAssets() + { + osg::ref_ptr workItem (new PreloadCommonAssetsWorkItem(mResourceSystem)); + mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures); + mWater->listAssetsToPreload(workItem->mTextures); + + workItem->mTextures.push_back("textures/_land_default.dds"); + + mWorkQueue->addWorkItem(workItem); + } + void RenderingManager::clearCache() { if (mTerrain.get()) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 8bc3a0a74..72f322d2c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,8 @@ namespace MWRender SceneUtil::WorkQueue* getWorkQueue(); + void preloadCommonAssets(); + void clearCache(); double getReferenceTime() const; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 0d3bf1a66..dafb2bb4b 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1694,6 +1694,46 @@ void SkyManager::setWaterHeight(float height) mUnderwaterSwitch->setWaterLevel(height); } +void SkyManager::listAssetsToPreload(std::vector& models, std::vector& textures) +{ + models.push_back("meshes/sky_atmosphere.nif"); + if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) + models.push_back("meshes/sky_night_02.nif"); + models.push_back("meshes/sky_night_01.nif"); + models.push_back("meshes/sky_clouds_01.nif"); + + models.push_back("meshes\\ashcloud.nif"); + models.push_back("meshes\\blightcloud.nif"); + models.push_back("meshes\\snow.nif"); + models.push_back("meshes\\blizzard.nif"); + + textures.push_back("textures/tx_mooncircle_full_s.dds"); + textures.push_back("textures/tx_mooncircle_full_m.dds"); + + textures.push_back("textures/tx_masser_new.dds"); + textures.push_back("textures/tx_masser_one_wax.dds"); + textures.push_back("textures/tx_masser_half_wax.dds"); + textures.push_back("textures/tx_masser_three_wax.dds"); + textures.push_back("textures/tx_masser_one_wan.dds"); + textures.push_back("textures/tx_masser_half_wan.dds"); + textures.push_back("textures/tx_masser_three_wan.dds"); + textures.push_back("textures/tx_masser_full.dds"); + + textures.push_back("textures/tx_secunda_new.dds"); + textures.push_back("textures/tx_secunda_one_wax.dds"); + textures.push_back("textures/tx_secunda_half_wax.dds"); + textures.push_back("textures/tx_secunda_three_wax.dds"); + textures.push_back("textures/tx_secunda_one_wan.dds"); + textures.push_back("textures/tx_secunda_half_wan.dds"); + textures.push_back("textures/tx_secunda_three_wan.dds"); + textures.push_back("textures/tx_secunda_full.dds"); + + textures.push_back("textures/tx_sun_05.dds"); + textures.push_back("textures/tx_sun_flash_grey_05.dds"); + + textures.push_back("textures/tx_raindrop_01.dds"); +} + void SkyManager::setWaterEnabled(bool enabled) { mUnderwaterSwitch->setEnabled(enabled); diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 0caadaa07..741911b23 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -153,6 +153,8 @@ namespace MWRender /// Set height of water plane (used to remove underwater weather particles) void setWaterHeight(float height); + void listAssetsToPreload(std::vector& models, std::vector& textures); + private: void create(); ///< no need to call this, automatically done on first enable() diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0e4f1a974..a8d601f11 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -649,6 +649,18 @@ Water::~Water() } } +void Water::listAssetsToPreload(std::vector &textures) +{ + int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount"); + std::string texture = mFallback->getFallbackString("Water_SurfaceTexture"); + for (int i=0; i +#include #include #include @@ -85,6 +86,8 @@ namespace MWRender const std::string& resourcePath); ~Water(); + void listAssetsToPreload(std::vector& textures); + void setEnabled(bool enabled); bool toggle(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9064dc94c..137dac42e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3216,4 +3216,9 @@ namespace MWWorld return mPhysics->getHitDistance(weaponPos, target); } + void World::preloadCommonAssets() + { + mRendering->preloadCommonAssets(); + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8c56443f7..cf9321da5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -183,6 +183,8 @@ namespace MWWorld virtual void startNewGame (bool bypass); ///< \param bypass Bypass regular game start. + virtual void preloadCommonAssets(); + virtual void clear(); virtual int countSavedGameRecords() const; From 10a3e270a3d32aaa9f34e575f544dfd2f3e4e922 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 01:51:23 +0100 Subject: [PATCH 46/66] Preload fast travel destinations --- apps/openmw/mwworld/scene.cpp | 87 +++++++++++++++++++++++++++++++---- apps/openmw/mwworld/scene.hpp | 3 ++ 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 2856c77e8..130644d7f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -633,6 +633,7 @@ namespace MWWorld { preloadTeleportDoorDestinations(); preloadExteriorGrid(); + preloadFastTravelDestinations(); } void Scene::preloadTeleportDoorDestinations() @@ -664,19 +665,12 @@ namespace MWWorld try { if (!door.getCellRef().getDestCell().empty()) - mPreloader->preload(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell()), mRendering.getReferenceTime()); + preloadCell(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell())); else { int x,y; MWBase::Environment::get().getWorld()->positionToIndex (door.getCellRef().getDoorDest().pos[0], door.getCellRef().getDoorDest().pos[1], x, y); - - for (int dx = -mHalfGridSize; dx <= mHalfGridSize; ++dx) - { - for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy) - { - mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(x+dx, y+dy), mRendering.getReferenceTime()); - } - } + preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); } } catch (std::exception& e) @@ -717,7 +711,80 @@ namespace MWWorld float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; if (dist < loadDist) - mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy), mRendering.getReferenceTime()); + preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy)); + } + } + } + + void Scene::preloadCell(CellStore *cell, bool preloadSurrounding) + { + if (preloadSurrounding && cell->isExterior()) + { + int x = cell->getCell()->getGridX(); + int y = cell->getCell()->getGridY(); + for (int dx = -mHalfGridSize; dx <= mHalfGridSize; ++dx) + { + for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy) + { + mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(x+dx, y+dy), mRendering.getReferenceTime()); + } + } + } + else + mPreloader->preload(cell, mRendering.getReferenceTime()); + } + + struct ListFastTravelDestinationsVisitor + { + ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) + : mPreloadDist(preloadDist) + , mPlayerPos(playerPos) + { + } + + bool operator()(const MWWorld::Ptr& ptr) + { + if ((ptr.getRefData().getPosition().asVec3() - mPlayerPos).length2() > mPreloadDist * mPreloadDist) + return true; + + if (ptr.getClass().isNpc()) + { + const std::vector& transport = ptr.get()->mBase->mTransport.mList; + mList.insert(mList.begin(), transport.begin(), transport.end()); + } + else + { + const std::vector& transport = ptr.get()->mBase->mTransport.mList; + mList.insert(mList.begin(), transport.begin(), transport.end()); + } + return true; + } + float mPreloadDist; + osg::Vec3f mPlayerPos; + std::vector mList; + }; + + void Scene::preloadFastTravelDestinations() + { + const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); + + for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); iter!=mActiveCells.end(); ++iter) + { + MWWorld::CellStore* cellStore = *iter; + cellStore->forEachType(listVisitor); + cellStore->forEachType(listVisitor); + } + + for (std::vector::const_iterator it = listVisitor.mList.begin(); it != listVisitor.mList.end(); ++it) + { + if (!it->mCellName.empty()) + preloadCell(MWBase::Environment::get().getWorld()->getInterior(it->mCellName)); + else + { + int x,y; + MWBase::Environment::get().getWorld()->positionToIndex( it->mPos.pos[0], it->mPos.pos[1], x, y); + preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); } } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 34e137e45..f473f7a3c 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -74,6 +74,9 @@ namespace MWWorld void preloadCells(); void preloadTeleportDoorDestinations(); void preloadExteriorGrid(); + void preloadFastTravelDestinations(); + + void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); public: From 6806741d9b9d1e5e613d667771c018baf4165af2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 01:58:07 +0100 Subject: [PATCH 47/66] Add settings for disabling the individual preloading types --- apps/openmw/mwworld/scene.cpp | 12 +++++++++--- apps/openmw/mwworld/scene.hpp | 4 ++++ files/settings-default.cfg | 11 ++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 130644d7f..3658bff35 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -457,6 +457,9 @@ namespace MWWorld , mCellLoadingThreshold(1024.f) , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) + , mPreloadExteriorGrid(Settings::Manager::getBool("preload exterior grid", "Cells")) + , mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells")) + , mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells")) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); mPreloader->setWorkQueue(mRendering.getWorkQueue()); @@ -631,9 +634,12 @@ namespace MWWorld void Scene::preloadCells() { - preloadTeleportDoorDestinations(); - preloadExteriorGrid(); - preloadFastTravelDestinations(); + if (mPreloadDoors) + preloadTeleportDoorDestinations(); + if (mPreloadExteriorGrid) + preloadExteriorGrid(); + if (mPreloadFastTravel) + preloadFastTravelDestinations(); } void Scene::preloadTeleportDoorDestinations() diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index f473f7a3c..5c429f7c7 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -64,6 +64,10 @@ namespace MWWorld float mPreloadDistance; bool mPreloadEnabled; + bool mPreloadExteriorGrid; + bool mPreloadDoors; + bool mPreloadFastTravel; + void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4df20e709..4a9a5dd65 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -37,9 +37,18 @@ first person field of view = 55.0 # dramatically affect performance, see documentation for details. exterior cell load distance = 1 -# Preload cells in a background thread +# Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled. preload enabled = true +# Preload adjacent cells when moving close to an exterior cell border. +preload exterior grid = true + +# Preload possible fast travel destinations. +preload fast travel = true + +# Preload the locations that doors lead to. +preload doors = true + # Preloading distance threshold preload distance = 1000 From f6f9eff9a68e5ef7297ef97323c5096aab4f490e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 03:06:00 +0100 Subject: [PATCH 48/66] Preload levelled creatures --- apps/openmw/mwclass/creaturelevlist.cpp | 17 +++++++++++++++++ apps/openmw/mwclass/creaturelevlist.hpp | 3 +++ apps/openmw/mwworld/scene.cpp | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index db2ba45bc..e2f29ea72 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -6,6 +6,7 @@ #include "../mwmechanics/levelledlist.hpp" #include "../mwworld/customdata.hpp" +#include "../mwmechanics/creaturestats.hpp" namespace MWClass { @@ -53,6 +54,22 @@ namespace MWClass registerClass (typeid (ESM::CreatureLevList).name(), instance); } + void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector &models) const + { + const MWWorld::LiveCellRef *ref = ptr.get(); + + for (std::vector::const_iterator it = ref->mBase->mList.begin(); it != ref->mBase->mList.end(); ++it) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (it->mLevel > player.getClass().getCreatureStats(player).getLevel()) + continue; + + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::ManualRef ref(store, it->mId); + ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models); + } + } + void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const { ensureCustomData(ptr); diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index 67a7858d8..25b5cbddf 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -17,6 +17,9 @@ namespace MWClass static void registerSelf(); + virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector& models) const; + ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3658bff35..4c843133d 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -197,7 +197,7 @@ namespace MWWorld if (mPreloadEnabled) { mPreloadTimer += duration; - if (mPreloadTimer > 0.5f) + if (mPreloadTimer > 0.25f) { preloadCells(); mPreloadTimer = 0.f; From d11c2864df515afe083628c781b7058f14fe1375 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 15:30:53 +0100 Subject: [PATCH 49/66] Introduce UnrefQueue to handle the deleting of no longer needed objects in the background thread --- apps/openmw/mwrender/objects.cpp | 12 +++++- apps/openmw/mwrender/objects.hpp | 13 +++++-- apps/openmw/mwrender/renderingmanager.cpp | 8 +++- apps/openmw/mwrender/renderingmanager.hpp | 2 + components/CMakeLists.txt | 2 +- components/sceneutil/unrefqueue.cpp | 46 +++++++++++++++++++++++ components/sceneutil/unrefqueue.hpp | 37 ++++++++++++++++++ components/terrain/terraingrid.cpp | 21 ++++++----- components/terrain/terraingrid.hpp | 9 ++--- 9 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 components/sceneutil/unrefqueue.cpp create mode 100644 components/sceneutil/unrefqueue.hpp 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; }; } From 40a6e05e178b28ddbdf8c909f7aab2dce7fa9af0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 16:09:55 +0100 Subject: [PATCH 50/66] Use a deque instead of vector in UnrefQueue --- components/sceneutil/unrefqueue.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 23b1359e5..0b65c4a32 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -1,5 +1,7 @@ #include "unrefqueue.hpp" +#include + #include //#include //#include @@ -12,7 +14,7 @@ namespace SceneUtil class UnrefWorkItem : public SceneUtil::WorkItem { public: - std::vector > mObjects; + std::deque > mObjects; virtual void doWork() { From ae031b23d42d7174f5c1187d79e1e47040d1e9df Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 16:10:42 +0100 Subject: [PATCH 51/66] Do not detach NPC parts in destructor --- apps/openmw/mwrender/animation.cpp | 7 ++++++- apps/openmw/mwrender/animation.hpp | 3 +++ apps/openmw/mwrender/npcanimation.cpp | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b7c0c0a36..3d4fd11d0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1409,8 +1409,13 @@ namespace MWRender PartHolder::~PartHolder() { - if (mNode->getNumParents()) + if (mNode.get() && mNode->getNumParents()) mNode->getParent(0)->removeChild(mNode); } + void PartHolder::unlink() + { + mNode = NULL; + } + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 992462e1f..feaf0e16c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -55,6 +55,9 @@ public: ~PartHolder(); + /// Unreferences mNode *without* detaching it from the graph. Only use if you know what you are doing. + void unlink(); + osg::ref_ptr getNode() { return mNode; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 026b3a2cf..10c0032d4 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -273,6 +273,13 @@ NpcAnimation::~NpcAnimation() // all from within this destructor. ouch! && mPtr.getRefData().getCustomData() && mPtr.getClass().getInventoryStore(mPtr).getListener() == this) mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); + + // do not detach (delete) parts yet, this is done so the background thread can handle the deletion + for(size_t i = 0;i < ESM::PRT_Count;i++) + { + if (mObjectParts[i].get()) + mObjectParts[i]->unlink(); + } } NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem, From 8ece1885cdc2662ceaefcd4542abdf9970880483 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 16:18:19 +0100 Subject: [PATCH 52/66] Animation: don't create the NodeMap if we don't need it --- apps/openmw/mwrender/animation.cpp | 61 +++++++++++++++++------------- apps/openmw/mwrender/animation.hpp | 5 ++- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3d4fd11d0..5aea13660 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -95,7 +95,12 @@ namespace class NodeMapVisitor : public osg::NodeVisitor { public: - NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + typedef std::map > NodeMap; + + NodeMapVisitor(NodeMap& map) + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + , mMap(map) + {} void apply(osg::MatrixTransform& trans) { @@ -103,15 +108,8 @@ namespace traverse(trans); } - typedef std::map > NodeMap; - - const NodeMap& getNodeMap() const - { - return mMap; - } - private: - NodeMap mMap; + NodeMap& mMap; }; NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) @@ -312,6 +310,7 @@ namespace MWRender Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem) : mInsert(parentNode) , mSkeleton(NULL) + , mNodeMapCreated(false) , mPtr(ptr) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) @@ -407,12 +406,14 @@ namespace MWRender if (!animsrc->mKeyframes || animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty()) return; + const NodeMap& nodeMap = getNodeMap(); + for (NifOsg::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin(); it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it) { std::string bonename = Misc::StringUtils::lowerCase(it->first); - NodeMap::const_iterator found = mNodeMap.find(bonename); - if (found == mNodeMap.end()) + NodeMap::const_iterator found = nodeMap.find(bonename); + if (found == nodeMap.end()) { std::cerr << "addAnimSource: can't find bone '" + bonename << "' in " << model << " (referenced by " << kfname << ")" << std::endl; continue; @@ -436,11 +437,11 @@ namespace MWRender if (!mAccumRoot) { - NodeMap::const_iterator found = mNodeMap.find("root bone"); - if (found == mNodeMap.end()) - found = mNodeMap.find("bip01"); + NodeMap::const_iterator found = nodeMap.find("root bone"); + if (found == nodeMap.end()) + found = nodeMap.find("bip01"); - if (found != mNodeMap.end()) + if (found != nodeMap.end()) mAccumRoot = found->second; } } @@ -690,6 +691,17 @@ namespace MWRender mTextKeyListener = listener; } + const Animation::NodeMap &Animation::getNodeMap() const + { + if (!mNodeMapCreated && mObjectRoot) + { + NodeMapVisitor visitor(mNodeMap); + mObjectRoot->accept(visitor); + mNodeMapCreated = true; + } + return mNodeMap; + } + void Animation::resetActiveGroups() { // remove all previous external controllers from the scene graph @@ -729,7 +741,7 @@ namespace MWRender for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it) { - osg::ref_ptr node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource + osg::ref_ptr node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource node->addUpdateCallback(it->second); mActiveControllers.insert(std::make_pair(node, it->second)); @@ -978,6 +990,7 @@ namespace MWRender mSkeleton = NULL; mNodeMap.clear(); + mNodeMapCreated = false; mActiveControllers.clear(); mAccumRoot = NULL; mAccumCtrl = NULL; @@ -1025,10 +1038,6 @@ namespace MWRender removeTriBipVisitor.remove(); } - NodeMapVisitor visitor; - mObjectRoot->accept(visitor); - mNodeMap = visitor.getNodeMap(); - mObjectRoot->addCullCallback(new SceneUtil::LightListCallback); } @@ -1121,8 +1130,8 @@ namespace MWRender parentNode = mInsert; else { - NodeMap::iterator found = mNodeMap.find(Misc::StringUtils::lowerCase(bonename)); - if (found == mNodeMap.end()) + NodeMap::const_iterator found = getNodeMap().find(Misc::StringUtils::lowerCase(bonename)); + if (found == getNodeMap().end()) throw std::runtime_error("Can't find bone " + bonename); parentNode = found->second; @@ -1222,8 +1231,8 @@ namespace MWRender const osg::Node* Animation::getNode(const std::string &name) const { std::string lowerName = Misc::StringUtils::lowerCase(name); - NodeMap::const_iterator found = mNodeMap.find(lowerName); - if (found == mNodeMap.end()) + NodeMap::const_iterator found = getNodeMap().find(lowerName); + if (found == getNodeMap().end()) return NULL; else return found->second; @@ -1317,8 +1326,8 @@ namespace MWRender if (mPtr.getClass().isBipedal(mPtr)) { - NodeMap::iterator found = mNodeMap.find("bip01 head"); - if (found != mNodeMap.end() && dynamic_cast(found->second.get())) + NodeMap::const_iterator found = getNodeMap().find("bip01 head"); + if (found != getNodeMap().end() && dynamic_cast(found->second.get())) { osg::Node* node = found->second; mHeadController = new RotateController(mObjectRoot.get()); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index feaf0e16c..1d7930566 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -232,7 +232,8 @@ protected: // Stored in all lowercase for a case-insensitive lookup typedef std::map > NodeMap; - NodeMap mNodeMap; + mutable NodeMap mNodeMap; + mutable bool mNodeMapCreated; MWWorld::Ptr mPtr; @@ -263,6 +264,8 @@ protected: float mAlpha; + const NodeMap& getNodeMap() const; + /* Sets the appropriate animations on the bone groups based on priority. */ void resetActiveGroups(); From ce3cce24a56df230616f13622deb59464b4b36d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 16:20:17 +0100 Subject: [PATCH 53/66] Remove unneeded dynamic_cast --- apps/openmw/mwrender/animation.cpp | 4 ++-- apps/openmw/mwrender/npcanimation.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 5aea13660..c86878e3d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1327,9 +1327,9 @@ namespace MWRender if (mPtr.getClass().isBipedal(mPtr)) { NodeMap::const_iterator found = getNodeMap().find("bip01 head"); - if (found != getNodeMap().end() && dynamic_cast(found->second.get())) + if (found != getNodeMap().end()) { - osg::Node* node = found->second; + osg::MatrixTransform* node = found->second; mHeadController = new RotateController(mObjectRoot.get()); node->addUpdateCallback(mHeadController); mActiveControllers.insert(std::make_pair(node, mHeadController)); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 10c0032d4..709653cd8 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -847,9 +847,9 @@ void NpcAnimation::addControllers() if (mViewMode == VM_FirstPerson) { NodeMap::iterator found = mNodeMap.find("bip01 neck"); - if (found != mNodeMap.end() && dynamic_cast(found->second.get())) + if (found != mNodeMap.end()) { - osg::Node* node = found->second; + osg::MatrixTransform* node = found->second.get(); mFirstPersonNeckController = new NeckController(mObjectRoot.get()); node->addUpdateCallback(mFirstPersonNeckController); mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController)); From 2e62298bd33534a64e7eff91c94ea1aac5f806ae Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 18:11:07 +0100 Subject: [PATCH 54/66] Clean up ObjectCache includes --- components/resource/niffilemanager.cpp | 2 ++ components/resource/objectcache.cpp | 2 ++ components/resource/objectcache.hpp | 13 +++++++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp index ecb63db60..82768cc2c 100644 --- a/components/resource/niffilemanager.cpp +++ b/components/resource/niffilemanager.cpp @@ -1,5 +1,7 @@ #include "niffilemanager.hpp" +#include + #include #include "objectcache.hpp" diff --git a/components/resource/objectcache.cpp b/components/resource/objectcache.cpp index 17368f34f..a0a3cf3a5 100644 --- a/components/resource/objectcache.cpp +++ b/components/resource/objectcache.cpp @@ -13,6 +13,8 @@ #include "objectcache.hpp" +#include + namespace Resource { diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index b933933f3..79ebabd5b 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -17,13 +17,18 @@ #ifndef OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE #define OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE -#include - -#include -#include +#include +#include +#include #include +namespace osg +{ + class Object; + class State; +} + namespace Resource { class ObjectCache : public osg::Referenced From e28dc3e72fea00ebde993cdb5cc3ea01e84d1b7c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 18:33:02 +0100 Subject: [PATCH 55/66] 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; From 246566cef4294062e8bc636458e4f54fffab56e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 18:48:49 +0100 Subject: [PATCH 56/66] Preload instances in BulletShapeManager --- apps/openmw/mwphysics/physicssystem.cpp | 4 +-- apps/openmw/mwworld/cellpreloader.cpp | 4 +-- components/resource/bulletshapemanager.cpp | 32 +++++++++++++++++++++- components/resource/bulletshapemanager.hpp | 18 ++++++++++-- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 6417968d3..5d0ac1f8f 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1088,7 +1088,7 @@ namespace MWPhysics void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType) { - osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + osg::ref_ptr shapeInstance = mShapeManager->getInstance(mesh); if (!shapeInstance || !shapeInstance->getCollisionShape()) return; @@ -1234,7 +1234,7 @@ namespace MWPhysics } void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { - osg::ref_ptr shapeInstance = mShapeManager->createInstance(mesh); + osg::ref_ptr shapeInstance = mShapeManager->getInstance(mesh); if (!shapeInstance) return; diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index c6fa3b949..047c1e99b 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -87,7 +87,7 @@ namespace MWWorld //std::cout << "preloading " << mesh << std::endl; mPreloadedNodes.push_back(mSceneManager->cacheInstance(mesh)); - mPreloadedShapes.push_back(mBulletShapeManager->getShape(mesh)); + mPreloadedShapes.push_back(mBulletShapeManager->cacheInstance(mesh)); size_t slashpos = mesh.find_last_of("/\\"); if (slashpos != std::string::npos && slashpos != mesh.size()-1) @@ -123,7 +123,7 @@ namespace MWWorld // keep a ref to the loaded object to make sure it stays loaded as long as this cell is in the preloaded state std::vector > mPreloadedNodes; - std::vector > mPreloadedShapes; + std::vector > mPreloadedShapes; std::vector > mPreloadedKeyframes; }; diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index cb57d686f..111808e6e 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -14,7 +14,7 @@ #include "scenemanager.hpp" #include "niffilemanager.hpp" #include "objectcache.hpp" - +#include "multiobjectcache.hpp" namespace Resource { @@ -98,6 +98,7 @@ private: BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) : ResourceManager(vfs) + , mInstanceCache(new MultiObjectCache) , mSceneManager(sceneMgr) , mNifFileManager(nifFileManager) { @@ -151,6 +152,28 @@ osg::ref_ptr BulletShapeManager::getShape(const std::string & return shape; } +osg::ref_ptr BulletShapeManager::cacheInstance(const std::string &name) +{ + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr instance = createInstance(normalized); + mInstanceCache->addEntryToObjectCache(normalized, instance.get()); + return instance; +} + +osg::ref_ptr BulletShapeManager::getInstance(const std::string &name) +{ + std::string normalized = name; + mVFS->normalizeFilename(normalized); + + osg::ref_ptr obj = mInstanceCache->takeFromObjectCache(normalized); + if (obj.get()) + return static_cast(obj.get()); + else + return createInstance(normalized); +} + osg::ref_ptr BulletShapeManager::createInstance(const std::string &name) { osg::ref_ptr shape = getShape(name); @@ -160,4 +183,11 @@ osg::ref_ptr BulletShapeManager::createInstance(const std:: return osg::ref_ptr(); } +void BulletShapeManager::updateCache(double referenceTime) +{ + ResourceManager::updateCache(referenceTime); + + mInstanceCache->removeUnreferencedObjectsInCache(); +} + } diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index 4c0cb1fd2..14b26962b 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -17,6 +17,8 @@ namespace Resource class BulletShape; class BulletShapeInstance; + class MultiObjectCache; + /// 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. @@ -26,12 +28,24 @@ namespace Resource BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); ~BulletShapeManager(); + /// @note May return a null pointer if the object has no shape. osg::ref_ptr getShape(const std::string& name); - /// Shorthand for getShape(name)->makeInstance(); - osg::ref_ptr createInstance(const std::string& name); + /// Create an instance of the given shape and cache it for later use, so that future calls to getInstance() can simply return + /// the cached instance instead of having to create a new one. + /// @note The returned ref_ptr may be kept by the caller to ensure that the instance stays in cache for as long as needed. + osg::ref_ptr cacheInstance(const std::string& name); + + /// @note May return a null pointer if the object has no shape. + osg::ref_ptr getInstance(const std::string& name); + + /// @see ResourceManager::updateCache + virtual void updateCache(double referenceTime); private: + osg::ref_ptr createInstance(const std::string& name); + + osg::ref_ptr mInstanceCache; SceneManager* mSceneManager; NifFileManager* mNifFileManager; }; From 3552b3a82cf706c8ba63a663a8d9144d90f7505c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 18:51:17 +0100 Subject: [PATCH 57/66] Don't create a BulletShapeInstance for actors --- apps/openmw/mwphysics/actor.cpp | 2 +- apps/openmw/mwphysics/actor.hpp | 4 ++-- apps/openmw/mwphysics/physicssystem.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index cc46897d1..c99754b5c 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -16,7 +16,7 @@ namespace MWPhysics { -Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) +Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) : mCanWaterWalk(false), mWalkingOnWater(false) , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false) , mInternalCollisionMode(true) diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 03193c675..5755def74 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -15,7 +15,7 @@ class btCollisionObject; namespace Resource { - class BulletShapeInstance; + class BulletShape; } namespace MWPhysics @@ -48,7 +48,7 @@ namespace MWPhysics class Actor : public PtrHolder { public: - Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world); + Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world); ~Actor(); /** diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 5d0ac1f8f..48b3426b8 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1234,11 +1234,11 @@ namespace MWPhysics } void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { - osg::ref_ptr shapeInstance = mShapeManager->getInstance(mesh); - if (!shapeInstance) + osg::ref_ptr shape = mShapeManager->getShape(mesh); + if (!shape) return; - Actor* actor = new Actor(ptr, shapeInstance, mCollisionWorld); + Actor* actor = new Actor(ptr, shape, mCollisionWorld); mActors.insert(std::make_pair(ptr, actor)); } From afe533e6709496131cd5f424cab3f9b1b538fc25 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 19:00:30 +0100 Subject: [PATCH 58/66] Accept a const Object in UnrefQueue --- components/sceneutil/unrefqueue.cpp | 4 ++-- components/sceneutil/unrefqueue.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 0b65c4a32..a5eb1654e 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -14,7 +14,7 @@ namespace SceneUtil class UnrefWorkItem : public SceneUtil::WorkItem { public: - std::deque > mObjects; + std::deque > mObjects; virtual void doWork() { @@ -30,7 +30,7 @@ namespace SceneUtil mWorkItem = new UnrefWorkItem; } - void UnrefQueue::push(osg::Object *obj) + void UnrefQueue::push(const osg::Object *obj) { mWorkItem->mObjects.push_back(obj); } diff --git a/components/sceneutil/unrefqueue.hpp b/components/sceneutil/unrefqueue.hpp index d0bec061c..85b1d31e1 100644 --- a/components/sceneutil/unrefqueue.hpp +++ b/components/sceneutil/unrefqueue.hpp @@ -22,7 +22,7 @@ namespace SceneUtil UnrefQueue(); /// Adds an object to the list of objects to be unreferenced. Call from the main thread. - void push(osg::Object* obj); + void push(const 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. From 1457a0de78464a68e9b56e0ccf33189f5e0afc33 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 19:04:59 +0100 Subject: [PATCH 59/66] Use the UnrefQueue to delete BulletShapeInstances --- apps/openmw/mwphysics/physicssystem.cpp | 14 ++++++++++++++ apps/openmw/mwphysics/physicssystem.hpp | 9 +++++++-- apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/scene.cpp | 2 ++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 48b3426b8..9f1cfc682 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -22,6 +22,7 @@ #include #include +#include #include // FindRecIndexVisitor @@ -533,6 +534,11 @@ namespace MWPhysics setOrigin(btVector3(pos[0], pos[1], pos[2])); } + const Resource::BulletShapeInstance* getShapeInstance() const + { + return mShapeInstance.get(); + } + void setScale(float scale) { mShapeInstance->getCollisionShape()->setLocalScaling(btVector3(scale,scale,scale)); @@ -690,6 +696,11 @@ namespace MWPhysics delete mBroadphase; } + void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) + { + mUnrefQueue = unrefQueue; + } + Resource::BulletShapeManager *PhysicsSystem::getShapeManager() { return mShapeManager.get(); @@ -1109,6 +1120,9 @@ namespace MWPhysics { mCollisionWorld->removeCollisionObject(found->second->getCollisionObject()); + if (mUnrefQueue.get()) + mUnrefQueue->push(found->second->getShapeInstance()); + mAnimatedObjects.erase(found->second); delete found->second; diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index a866717c7..110b59268 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -25,11 +25,12 @@ namespace MWRender namespace Resource { class BulletShapeManager; + class ResourceSystem; } -namespace Resource +namespace SceneUtil { - class ResourceSystem; + class UnrefQueue; } class btCollisionWorld; @@ -53,6 +54,8 @@ namespace MWPhysics PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode); ~PhysicsSystem (); + void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); + Resource::BulletShapeManager* getShapeManager(); void enableWater(float height); @@ -165,6 +168,8 @@ namespace MWPhysics void updateWater(); + osg::ref_ptr mUnrefQueue; + btBroadphaseInterface* mBroadphase; btDefaultCollisionConfiguration* mCollisionConfiguration; btCollisionDispatcher* mDispatcher; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5a9cd59fc..be3caaeb4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -263,6 +263,11 @@ namespace MWRender return mWorkQueue.get(); } + SceneUtil::UnrefQueue *RenderingManager::getUnrefQueue() + { + return mUnrefQueue.get(); + } + void RenderingManager::preloadCommonAssets() { osg::ref_ptr workItem (new PreloadCommonAssetsWorkItem(mResourceSystem)); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 3d6a4ac3a..4c850de12 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,7 @@ namespace MWRender Resource::ResourceSystem* getResourceSystem(); SceneUtil::WorkQueue* getWorkQueue(); + SceneUtil::UnrefQueue* getUnrefQueue(); void preloadCommonAssets(); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 46d75daaf..b0e384493 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -466,6 +466,8 @@ namespace MWWorld mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); mPreloader->setWorkQueue(mRendering.getWorkQueue()); + mPhysics->setUnrefQueue(rendering.getUnrefQueue()); + float cacheExpiryDelay = Settings::Manager::getFloat("cache expiry delay", "Cells"); rendering.getResourceSystem()->setExpiryDelay(cacheExpiryDelay); mPreloader->setExpiryDelay(cacheExpiryDelay); From 596fe56bfdfe95dbc1a8783f65bf33c73694cd7e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 20:14:16 +0100 Subject: [PATCH 60/66] Make Land::loadData thread safe --- apps/opencs/model/tools/mergestages.cpp | 2 -- apps/openmw/mwrender/terrainstorage.cpp | 3 ++ components/esm/loadland.cpp | 42 ++++++++++++++----------- components/esm/loadland.hpp | 7 +++-- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/apps/opencs/model/tools/mergestages.cpp b/apps/opencs/model/tools/mergestages.cpp index 52e1e6964..f52e8886f 100644 --- a/apps/opencs/model/tools/mergestages.cpp +++ b/apps/opencs/model/tools/mergestages.cpp @@ -224,8 +224,6 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::Land newLand (land); - newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway, - // because record is already fully loaded) newLand.mPlugin = 0; if (land.mDataTypes & ESM::Land::DATA_VTEX) diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index ed1d8b677..02947b5dd 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -62,6 +62,9 @@ namespace MWRender const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; if (!land->isDataLoaded(flags)) land->loadData(flags); + + // TODO: unload land data when it's no longer needed + return land; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index e7be03321..b0b072232 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -2,6 +2,8 @@ #include +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" @@ -60,7 +62,6 @@ namespace ESM , mX(0) , mY(0) , mPlugin(0) - , mEsm(NULL) , mDataTypes(0) , mDataLoaded(false) , mLandData(NULL) @@ -86,8 +87,7 @@ namespace ESM { isDeleted = false; - mEsm = &esm; - mPlugin = mEsm->getIndex(); + mPlugin = esm.getIndex(); bool hasLocation = false; bool isLoaded = false; @@ -180,6 +180,8 @@ namespace ESM void Land::loadData(int flags) const { + OpenThreads::ScopedLock lock(mMutex); + // Try to load only available data flags = flags & mDataTypes; // Return if all required data is loaded @@ -191,15 +193,17 @@ namespace ESM mLandData = new LandData; mLandData->mDataTypes = mDataTypes; } - mEsm->restoreContext(mContext); - if (mEsm->isNextSub("VNML")) { - condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); + ESM::ESMReader reader; + reader.restoreContext(mContext); + + if (reader.isNextSub("VNML")) { + condLoad(reader, flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); } - if (mEsm->isNextSub("VHGT")) { + if (reader.isNextSub("VHGT")) { static VHGT vhgt; - if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { + if (condLoad(reader, flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { rowOffset += vhgt.mHeightData[y * LAND_SIZE]; @@ -217,14 +221,14 @@ namespace ESM } } - if (mEsm->isNextSub("WNAM")) { - condLoad(flags, DATA_WNAM, mLandData->mWnam, 81); + if (reader.isNextSub("WNAM")) { + condLoad(reader, flags, DATA_WNAM, mLandData->mWnam, 81); } - if (mEsm->isNextSub("VCLR")) - condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); - if (mEsm->isNextSub("VTEX")) { + if (reader.isNextSub("VCLR")) + condLoad(reader, flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); + if (reader.isNextSub("VTEX")) { static uint16_t vtex[LAND_NUM_TEXTURES]; - if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) { + if (condLoad(reader, flags, DATA_VTEX, vtex, sizeof(vtex))) { LandData::transposeTextureData(vtex, mLandData->mTextures); } } @@ -240,25 +244,26 @@ namespace ESM } } - bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const + bool Land::condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const { if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { - mEsm->getHExact(ptr, size); + reader.getHExact(ptr, size); mDataLoaded |= dataFlag; return true; } - mEsm->skipHSubSize(size); + reader.skipHSubSize(size); return false; } bool Land::isDataLoaded(int flags) const { + OpenThreads::ScopedLock lock(mMutex); return (mDataLoaded & flags) == (flags & mDataTypes); } Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), - mEsm (land.mEsm), mContext (land.mContext), mDataTypes (land.mDataTypes), + mContext (land.mContext), mDataTypes (land.mDataTypes), mDataLoaded (land.mDataLoaded), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) {} @@ -275,7 +280,6 @@ namespace ESM std::swap (mX, land.mX); std::swap (mY, land.mY); std::swap (mPlugin, land.mPlugin); - std::swap (mEsm, land.mEsm); std::swap (mContext, land.mContext); std::swap (mDataTypes, land.mDataTypes); std::swap (mDataLoaded, land.mDataLoaded); diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 65ac88cda..8a8d6fdd2 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -3,6 +3,8 @@ #include +#include + #include "esmcommon.hpp" namespace ESM @@ -31,7 +33,6 @@ struct Land // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. - ESMReader* mEsm; ESM_Context mContext; int mDataTypes; @@ -155,7 +156,9 @@ struct Land /// Loads data and marks it as loaded /// \return true if data is actually loaded from file, false otherwise /// including the case when data is already loaded - bool condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const; + bool condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const; + + mutable OpenThreads::Mutex mMutex; mutable int mDataLoaded; From 8aba74e6ee4df45cc442148e4e0b0f770b2159ef Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 20:23:53 +0100 Subject: [PATCH 61/66] Remove GridElement --- components/terrain/terraingrid.cpp | 20 +++++--------------- components/terrain/terraingrid.hpp | 4 +--- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4b67371e0..4d2f421e6 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -63,12 +63,6 @@ TerrainGrid::~TerrainGrid() } } -class GridElement -{ -public: - osg::ref_ptr mNode; -}; - osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) { if (chunkSize * mNumSplits > 1.f) @@ -208,11 +202,9 @@ void TerrainGrid::loadCell(int x, int y) if (!terrainNode) return; // no terrain defined - std::auto_ptr element (new GridElement); - element->mNode = terrainNode; - mTerrainRoot->addChild(element->mNode); + mTerrainRoot->addChild(terrainNode); - mGrid[std::make_pair(x,y)] = element.release(); + mGrid[std::make_pair(x,y)] = terrainNode; } void TerrainGrid::unloadCell(int x, int y) @@ -221,13 +213,11 @@ void TerrainGrid::unloadCell(int x, int y) if (it == mGrid.end()) return; - GridElement* element = it->second; - mTerrainRoot->removeChild(element->mNode); + osg::Node* terrainNode = it->second; + mTerrainRoot->removeChild(terrainNode); if (mUnrefQueue.get()) - mUnrefQueue->push(element->mNode); - - delete element; + mUnrefQueue->push(terrainNode); mGrid.erase(it); } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 5708e7068..4e6a78abd 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -14,8 +14,6 @@ namespace SceneUtil namespace Terrain { - class GridElement; - /// @brief Simple terrain implementation that loads cells in a grid, with no LOD class TerrainGrid : public Terrain::World { @@ -38,7 +36,7 @@ namespace Terrain typedef std::map > TextureCache; TextureCache mTextureCache; - typedef std::map, GridElement*> Grid; + typedef std::map, osg::ref_ptr > Grid; Grid mGrid; osg::ref_ptr mUnrefQueue; From 98848c752a0621e3820f62b53f6c8b46aee3fcc6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 20:26:58 +0100 Subject: [PATCH 62/66] Make getLayerInfo thread safe --- components/esmterrain/storage.cpp | 4 ++++ components/esmterrain/storage.hpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 267b831ec..cead73070 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include @@ -498,6 +500,8 @@ namespace ESMTerrain Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { + OpenThreads::ScopedLock lock(mLayerInfoMutex); + // Already have this cached? std::map::iterator found = mLayerInfoMap.find(texture); if (found != mLayerInfoMap.end()) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 5b8ca953d..9ca39e6f3 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -1,6 +1,8 @@ #ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H #define COMPONENTS_ESM_TERRAIN_STORAGE_H +#include + #include #include @@ -105,6 +107,7 @@ namespace ESMTerrain std::string getTextureName (UniqueTextureId id); std::map mLayerInfoMap; + OpenThreads::Mutex mLayerInfoMutex; Terrain::LayerInfo getLayerInfo(const std::string& texture); }; From 0865cea2111a3e641a4e78a3935adc4d3b2a649b Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 20:57:30 +0100 Subject: [PATCH 63/66] Preload terrain --- apps/openmw/mwrender/renderingmanager.cpp | 17 ++--- apps/openmw/mwrender/renderingmanager.hpp | 3 +- apps/openmw/mwworld/cellpreloader.cpp | 52 +++++++++---- apps/openmw/mwworld/cellpreloader.hpp | 8 +- apps/openmw/mwworld/scene.cpp | 4 +- components/resource/resourcemanager.cpp | 5 -- components/resource/resourcemanager.hpp | 3 - components/terrain/buffercache.cpp | 4 + components/terrain/buffercache.hpp | 4 + components/terrain/terraingrid.cpp | 92 +++++++++++++++++------ components/terrain/terraingrid.hpp | 19 ++++- components/terrain/world.cpp | 1 - components/terrain/world.hpp | 6 +- 13 files changed, 152 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index be3caaeb4..177cee4ea 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -258,16 +258,21 @@ namespace MWRender return mResourceSystem; } - SceneUtil::WorkQueue *RenderingManager::getWorkQueue() + SceneUtil::WorkQueue* RenderingManager::getWorkQueue() { return mWorkQueue.get(); } - SceneUtil::UnrefQueue *RenderingManager::getUnrefQueue() + SceneUtil::UnrefQueue* RenderingManager::getUnrefQueue() { return mUnrefQueue.get(); } + Terrain::World* RenderingManager::getTerrain() + { + return mTerrain.get(); + } + void RenderingManager::preloadCommonAssets() { osg::ref_ptr workItem (new PreloadCommonAssetsWorkItem(mResourceSystem)); @@ -279,12 +284,6 @@ namespace MWRender mWorkQueue->addWorkItem(workItem); } - void RenderingManager::clearCache() - { - if (mTerrain.get()) - mTerrain->clearCache(); - } - double RenderingManager::getReferenceTime() const { return mViewer->getFrameStamp()->getReferenceTime(); @@ -838,7 +837,7 @@ namespace MWRender void RenderingManager::updateTextureFiltering() { if (mTerrain.get()) - mTerrain->clearCache(); + mTerrain->updateCache(); mResourceSystem->getSceneManager()->setFilterSettings( Settings::Manager::getString("texture mag filter", "General"), diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 4c850de12..4dda6f273 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -73,11 +73,10 @@ namespace MWRender SceneUtil::WorkQueue* getWorkQueue(); SceneUtil::UnrefQueue* getUnrefQueue(); + Terrain::World* getTerrain(); void preloadCommonAssets(); - void clearCache(); - double getReferenceTime() const; osg::Group* getLightRoot(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 047c1e99b..e72c36c68 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -44,10 +45,14 @@ namespace MWWorld { public: /// Constructor to be called from the main thread. - PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager) - : mSceneManager(sceneManager) + PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain) + : mIsExterior(cell->getCell()->isExterior()) + , mX(cell->getCell()->getGridX()) + , mY(cell->getCell()->getGridY()) + , mSceneManager(sceneManager) , mBulletShapeManager(bulletShapeManager) , mKeyframeManager(keyframeManager) + , mTerrain(terrain) { osg::Timer timer; ListModelsVisitor visitor (mMeshes); @@ -86,8 +91,8 @@ namespace MWWorld //std::cout << "preloading " << mesh << std::endl; - mPreloadedNodes.push_back(mSceneManager->cacheInstance(mesh)); - mPreloadedShapes.push_back(mBulletShapeManager->cacheInstance(mesh)); + mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh)); + mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh)); size_t slashpos = mesh.find_last_of("/\\"); if (slashpos != std::string::npos && slashpos != mesh.size()-1) @@ -99,7 +104,7 @@ namespace MWWorld if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) { kfname.replace(kfname.size()-4, 4, ".kf"); - mPreloadedKeyframes.push_back(mKeyframeManager->get(kfname)); + mPreloadedObjects.push_back(mKeyframeManager->get(kfname)); } } @@ -111,29 +116,44 @@ namespace MWWorld // error will be shown when visiting the cell } } - std::cout << "preloaded " << mPreloadedNodes.size() << " nodes in " << preloadTimer.time_m() << std::endl; + + if (mIsExterior) + { + try + { + mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY)); + } + catch(std::exception& e) + { + } + } + + std::cout << "preloaded " << mPreloadedObjects.size() << " objects in " << preloadTimer.time_m() << std::endl; } private: typedef std::vector MeshList; + bool mIsExterior; + int mX; + int mY; MeshList mMeshes; Resource::SceneManager* mSceneManager; Resource::BulletShapeManager* mBulletShapeManager; Resource::KeyframeManager* mKeyframeManager; + Terrain::World* mTerrain; - // keep a ref to the loaded object to make sure it stays loaded as long as this cell is in the preloaded state - std::vector > mPreloadedNodes; - std::vector > mPreloadedShapes; - std::vector > mPreloadedKeyframes; + // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state + std::vector > mPreloadedObjects; }; /// Worker thread item: update the resource system's cache, effectively deleting unused entries. class UpdateCacheItem : public SceneUtil::WorkItem { public: - UpdateCacheItem(Resource::ResourceSystem* resourceSystem, double referenceTime) + UpdateCacheItem(Resource::ResourceSystem* resourceSystem, Terrain::World* terrain, double referenceTime) : mReferenceTime(referenceTime) , mResourceSystem(resourceSystem) + , mTerrain(terrain) { } @@ -141,17 +161,21 @@ namespace MWWorld { osg::Timer timer; mResourceSystem->updateCache(mReferenceTime); + + mTerrain->updateCache(); std::cout << "cleared cache in " << timer.time_m() << std::endl; } private: double mReferenceTime; Resource::ResourceSystem* mResourceSystem; + Terrain::World* mTerrain; }; - CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager) + CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain) : mResourceSystem(resourceSystem) , mBulletShapeManager(bulletShapeManager) + , mTerrain(terrain) , mExpiryDelay(0.0) { } @@ -177,7 +201,7 @@ namespace MWWorld return; } - osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager())); + osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain)); mWorkQueue->addWorkItem(item); mPreloadCells[cell] = PreloadEntry(timestamp, item); @@ -201,7 +225,7 @@ namespace MWWorld } // the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations - mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, timestamp)); + mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, mTerrain, timestamp)); } void CellPreloader::setExpiryDelay(double expiryDelay) diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 55e40dcbd..32ea194c6 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -11,6 +11,11 @@ namespace Resource class BulletShapeManager; } +namespace Terrain +{ + class World; +} + namespace MWWorld { class CellStore; @@ -18,7 +23,7 @@ namespace MWWorld class CellPreloader { public: - CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager); + CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain); /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell. /// @note The cell itself must be in State_Loaded or State_Preloaded. @@ -37,6 +42,7 @@ namespace MWWorld private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; + Terrain::World* mTerrain; osg::ref_ptr mWorkQueue; double mExpiryDelay; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b0e384493..0be26c7b6 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -416,7 +416,6 @@ namespace MWWorld mCellChanged = true; mPreloader->updateCache(mRendering.getReferenceTime()); - mRendering.clearCache(); std::cout << "changeCellGrid took " << timer.time_m() << std::endl; } @@ -463,7 +462,7 @@ namespace MWWorld , mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells")) , mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells")) { - mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager())); + mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain())); mPreloader->setWorkQueue(mRendering.getWorkQueue()); mPhysics->setUnrefQueue(rendering.getUnrefQueue()); @@ -549,7 +548,6 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); mPreloader->updateCache(mRendering.getReferenceTime()); - mRendering.clearCache(); std::cout << "change to interior took " << timer.time_m() << std::endl; } diff --git a/components/resource/resourcemanager.cpp b/components/resource/resourcemanager.cpp index e5f6d9f43..0a9784e6b 100644 --- a/components/resource/resourcemanager.cpp +++ b/components/resource/resourcemanager.cpp @@ -19,11 +19,6 @@ namespace Resource mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay); } - void ResourceManager::clearCache() - { - mCache->clear(); - } - void ResourceManager::setExpiryDelay(double expiryDelay) { mExpiryDelay = expiryDelay; diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp index d2a1d1023..34fce0145 100644 --- a/components/resource/resourcemanager.hpp +++ b/components/resource/resourcemanager.hpp @@ -22,9 +22,6 @@ namespace Resource /// 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(); - /// How long to keep objects in cache after no longer being referenced. void setExpiryDelay (double expiryDelay); diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index f9c860d47..2a72ea59b 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include "defs.hpp" @@ -178,6 +180,7 @@ namespace Terrain osg::ref_ptr BufferCache::getUVBuffer() { + OpenThreads::ScopedLock lock(mUvBufferMutex); if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) { return mUvBufferMap[mNumVerts]; @@ -206,6 +209,7 @@ namespace Terrain osg::ref_ptr BufferCache::getIndexBuffer(unsigned int flags) { + OpenThreads::ScopedLock lock(mIndexBufferMutex); unsigned int verts = mNumVerts; if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp index ca210f238..d1a57f811 100644 --- a/components/terrain/buffercache.hpp +++ b/components/terrain/buffercache.hpp @@ -17,8 +17,10 @@ namespace Terrain /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) + /// @note Thread safe. osg::ref_ptr getIndexBuffer (unsigned int flags); + /// @note Thread safe. osg::ref_ptr getUVBuffer(); // TODO: add releaseGLObjects() for our vertex/element buffer objects @@ -27,8 +29,10 @@ namespace Terrain // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need. std::map > mIndexBufferMap; + OpenThreads::Mutex mIndexBufferMutex; std::map > mUvBufferMap; + OpenThreads::Mutex mUvBufferMutex; unsigned int mNumVerts; }; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 4d2f421e6..5b1854abc 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include #include @@ -50,9 +52,9 @@ namespace Terrain 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) + , mCache((storage->getCellVertices()-1)/static_cast(mNumSplits) + 1) , mUnrefQueue(unrefQueue) { - mCache = BufferCache((storage->getCellVertices()-1)/static_cast(mNumSplits) + 1); } TerrainGrid::~TerrainGrid() @@ -63,6 +65,21 @@ TerrainGrid::~TerrainGrid() } } +osg::ref_ptr TerrainGrid::cacheCell(int x, int y) +{ + { + OpenThreads::ScopedLock lock(mGridCacheMutex); + Grid::iterator found = mGridCache.find(std::make_pair(x,y)); + if (found != mGridCache.end()) + return found->second; + } + osg::ref_ptr node = buildTerrain(NULL, 1.f, osg::Vec2f(x+0.5, y+0.5)); + + OpenThreads::ScopedLock lock(mGridCacheMutex); + mGridCache.insert(std::make_pair(std::make_pair(x,y), node)); + return node; +} + osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) { if (chunkSize * mNumSplits > 1.f) @@ -131,19 +148,22 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu unsigned int dummyTextureCounter = 0; std::vector > layerTextures; - for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { - osg::ref_ptr texture = mTextureCache[it->mDiffuseMap]; - if (!texture) + OpenThreads::ScopedLock lock(mTextureCacheMutex); + for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { - texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap)); - texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); - texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); - mResourceSystem->getSceneManager()->applyFilterSettings(texture); - mTextureCache[it->mDiffuseMap] = texture; + osg::ref_ptr texture = mTextureCache[it->mDiffuseMap]; + if (!texture) + { + texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap)); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + mResourceSystem->getSceneManager()->applyFilterSettings(texture); + mTextureCache[it->mDiffuseMap] = texture; + } + layerTextures.push_back(texture); + textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back()); } - layerTextures.push_back(texture); - textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back()); } std::vector > blendmapTextures; @@ -196,11 +216,27 @@ void TerrainGrid::loadCell(int x, int y) if (mGrid.find(std::make_pair(x, y)) != mGrid.end()) return; // already loaded - osg::Vec2f center(x+0.5f, y+0.5f); + // try to get it from the cache + osg::ref_ptr terrainNode; + { + OpenThreads::ScopedLock lock(mGridCacheMutex); + Grid::const_iterator found = mGridCache.find(std::make_pair(x,y)); + if (found != mGridCache.end()) + { + terrainNode = found->second; + if (!terrainNode) + return; // no terrain defined + } + } - osg::ref_ptr terrainNode = buildTerrain(NULL, 1.f, center); + // didn't find in cache, build it if (!terrainNode) - return; // no terrain defined + { + osg::Vec2f center(x+0.5f, y+0.5f); + terrainNode = buildTerrain(NULL, 1.f, center); + if (!terrainNode) + return; // no terrain defined + } mTerrainRoot->addChild(terrainNode); @@ -213,7 +249,7 @@ void TerrainGrid::unloadCell(int x, int y) if (it == mGrid.end()) return; - osg::Node* terrainNode = it->second; + osg::ref_ptr terrainNode = it->second; mTerrainRoot->removeChild(terrainNode); if (mUnrefQueue.get()) @@ -222,18 +258,28 @@ void TerrainGrid::unloadCell(int x, int y) mGrid.erase(it); } -void TerrainGrid::clearCache() +void TerrainGrid::updateCache() { - for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) { - if (it->second->referenceCount() <= 1) + OpenThreads::ScopedLock lock(mTextureCacheMutex); + for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) + { + if (it->second->referenceCount() <= 1) + mTextureCache.erase(it++); + else + ++it; + } + } + + { + OpenThreads::ScopedLock lock(mGridCacheMutex); + for (Grid::iterator it = mGridCache.begin(); it != mGridCache.end();) { - if (mUnrefQueue.get()) - mUnrefQueue->push(it->second); - mTextureCache.erase(it++); + if (it->second->referenceCount() <= 1) + mGridCache.erase(it++); + else + ++it; } - else - ++it; } } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 4e6a78abd..46a95e817 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -21,11 +21,20 @@ namespace Terrain TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue = NULL); ~TerrainGrid(); + /// Load a terrain cell and store it in cache for later use. + /// @note The returned ref_ptr should be kept by the caller to ensure that the terrain stays in cache for as long as needed. + /// @note Thread safe. + virtual osg::ref_ptr cacheCell(int x, int y); + + /// @note Not thread safe. virtual void loadCell(int x, int y); + + /// @note Not thread safe. virtual void unloadCell(int x, int y); - /// Clear cached textures that are no longer referenced - void clearCache(); + /// Clear cached objects that are no longer referenced + /// @note Thread safe. + void updateCache(); private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); @@ -35,10 +44,16 @@ namespace Terrain typedef std::map > TextureCache; TextureCache mTextureCache; + OpenThreads::Mutex mTextureCacheMutex; typedef std::map, osg::ref_ptr > Grid; Grid mGrid; + Grid mGridCache; + OpenThreads::Mutex mGridCacheMutex; + + BufferCache mCache; + osg::ref_ptr mUnrefQueue; }; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 2250b593d..b56e87e1d 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -11,7 +11,6 @@ namespace Terrain World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask) : mStorage(storage) - , mCache(storage->getCellVertices()) , mParent(parent) , mResourceSystem(resourceSystem) , mIncrementalCompileOperation(ico) diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index bd5d0a466..992438a6a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -39,10 +39,12 @@ namespace Terrain Storage* storage, int nodeMask); virtual ~World(); - virtual void clearCache() {} + virtual void updateCache() {} float getHeightAt (const osg::Vec3f& worldPos); + virtual osg::ref_ptr cacheCell(int x, int y) {return NULL;} + // This is only a hint and may be ignored by the implementation. virtual void loadCell(int x, int y) {} virtual void unloadCell(int x, int y) {} @@ -52,8 +54,6 @@ namespace Terrain protected: Storage* mStorage; - BufferCache mCache; - osg::ref_ptr mParent; osg::ref_ptr mTerrainRoot; From 9f729667fb064ab5d7380a103da5180a940279a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 21:06:02 +0100 Subject: [PATCH 64/66] Remove debug output --- apps/openmw/mwworld/cellpreloader.cpp | 11 ----------- apps/openmw/mwworld/scene.cpp | 7 ------- components/nifosg/controller.hpp | 1 - components/resource/scenemanager.cpp | 2 -- 4 files changed, 21 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index e72c36c68..ce9a87beb 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -54,7 +54,6 @@ namespace MWWorld , mKeyframeManager(keyframeManager) , mTerrain(terrain) { - osg::Timer timer; ListModelsVisitor visitor (mMeshes); if (cell->getState() == MWWorld::CellStore::State_Loaded) { @@ -73,15 +72,11 @@ namespace MWWorld mMeshes.push_back(model); } } - std::cout << "listed models in " << timer.time_m() << std::endl; } /// Preload work to be called from the worker thread. virtual void doWork() { - // TODO: make CellStore::loadRefs thread safe so we can call it from here - - osg::Timer preloadTimer; for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it) { try @@ -89,8 +84,6 @@ namespace MWWorld std::string mesh = *it; mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); - //std::cout << "preloading " << mesh << std::endl; - mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh)); mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh)); @@ -127,8 +120,6 @@ namespace MWWorld { } } - - std::cout << "preloaded " << mPreloadedObjects.size() << " objects in " << preloadTimer.time_m() << std::endl; } private: @@ -159,11 +150,9 @@ namespace MWWorld virtual void doWork() { - osg::Timer timer; mResourceSystem->updateCache(mReferenceTime); mTerrain->updateCache(); - std::cout << "cleared cache in " << timer.time_m() << std::endl; } private: diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0be26c7b6..308809bd2 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -332,7 +331,6 @@ namespace MWWorld void Scene::changeCellGrid (int X, int Y) { - osg::Timer timer; Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); @@ -416,8 +414,6 @@ namespace MWWorld mCellChanged = true; mPreloader->updateCache(mRendering.getReferenceTime()); - - std::cout << "changeCellGrid took " << timer.time_m() << std::endl; } void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) @@ -488,7 +484,6 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { - osg::Timer timer; CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); bool loadcell = (mCurrentCell == NULL); if(!loadcell) @@ -548,8 +543,6 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); mPreloader->updateCache(mRendering.getReferenceTime()); - - std::cout << "change to interior took " << timer.time_m() << std::endl; } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 0b633a122..ce268f587 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 5acd5f8fb..29f7076f0 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -366,8 +366,6 @@ namespace Resource if (mIncrementalCompileOperation) mIncrementalCompileOperation->add(loaded); - //std::cout << "loading " << normalized << std::endl; - mCache->addEntryToObjectCache(normalized, loaded); return loaded; } From 98c5e072f29990d8de48533ebed6f684dce7f5cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Feb 2016 21:17:10 +0100 Subject: [PATCH 65/66] Swap the terrain cache update order to make more sense --- components/terrain/terraingrid.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 5b1854abc..f40f3a388 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -261,22 +261,22 @@ void TerrainGrid::unloadCell(int x, int y) void TerrainGrid::updateCache() { { - OpenThreads::ScopedLock lock(mTextureCacheMutex); - for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) + OpenThreads::ScopedLock lock(mGridCacheMutex); + for (Grid::iterator it = mGridCache.begin(); it != mGridCache.end();) { if (it->second->referenceCount() <= 1) - mTextureCache.erase(it++); + mGridCache.erase(it++); else ++it; } } { - OpenThreads::ScopedLock lock(mGridCacheMutex); - for (Grid::iterator it = mGridCache.begin(); it != mGridCache.end();) + OpenThreads::ScopedLock lock(mTextureCacheMutex); + for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) { if (it->second->referenceCount() <= 1) - mGridCache.erase(it++); + mTextureCache.erase(it++); else ++it; } From 5e876b1379545964c59ea4c6a7bc27b4e320968f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 10 Feb 2016 15:34:06 +0100 Subject: [PATCH 66/66] Add missing include --- apps/openmw/mwrender/water.cpp | 1 + components/terrain/terraingrid.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index a8d601f11..dba85aeb7 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index f40f3a388..4f0e464ce 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include