diff --git a/src/osgDB/ObjectCache.cpp b/src/osgDB/ObjectCache.cpp index 217d3e5de..6a35eff33 100644 --- a/src/osgDB/ObjectCache.cpp +++ b/src/osgDB/ObjectCache.cpp @@ -11,6 +11,7 @@ * OpenSceneGraph Public License for more details. */ +#include #include #include @@ -188,15 +189,95 @@ void ObjectCache::clear() _objectCache.clear(); } +namespace ObjectCacheUtils +{ + +struct ContainsUnreffedTextures : public osg::NodeVisitor +{ + ContainsUnreffedTextures() : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + foundUnreffedTexture(false) + {} + + bool foundUnreffedTexture; + + bool check(const osg::Texture* texture) + { + if (!texture) return false; + + unsigned int numImages = 0; + for(unsigned int i=0; igetNumImages(); ++i) + { + if (texture->getImage(i)) ++numImages; + } + + return numImages==0; + } + + bool check(const osg::StateSet* stateset) + { + for(unsigned int i=0; igetNumTextureAttributeLists(); ++i) + { + const osg::StateAttribute* sa = stateset->getTextureAttribute(i, osg::StateAttribute::TEXTURE); + if (sa && check(sa->asTexture())) return true; + } + return false; + } + + bool check(osg::Object* object) + { + if (object->asStateAttribute()) return check(dynamic_cast(object)); + if (object->asStateSet()) return check(object->asStateSet()); + if (!object->asNode()) return false; + + foundUnreffedTexture = false; + + object->asNode()->accept(*this); + + return foundUnreffedTexture; + } + + void apply(osg::Node& node) + { + if (node.getStateSet()) + { + if (check(node.getStateSet())) + { + foundUnreffedTexture = true; + return; + } + } + + traverse(node); + } +}; + +} // ObjectCacheUtils + void ObjectCache::releaseGLObjects(osg::State* state) { OpenThreads::ScopedLock lock(_objectCacheMutex); + ObjectCacheUtils::ContainsUnreffedTextures cut; + for(ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); - ++itr) + ) { + ObjectCacheMap::iterator curr_itr = itr; + + // get object and advance iterator to next item osg::Object* object = itr->second.first.get(); + + bool needToRemoveEntry = cut.check(itr->second.first.get()); + object->releaseGLObjects(state); + + ++itr; + + if (needToRemoveEntry) + { + _objectCache.erase(curr_itr); + } } }