diff --git a/include/osg/Drawable b/include/osg/Drawable index 04ec9071b..3019efe44 100644 --- a/include/osg/Drawable +++ b/include/osg/Drawable @@ -22,6 +22,7 @@ namespace osg { + class Vec2f; class Vec3f; class Vec4f; @@ -115,6 +116,9 @@ class SG_EXPORT Drawable : public Object { public: + static unsigned int s_numberNewDrawablesInLastFrame; + static unsigned int s_numberDeletedDrawablesInLastFrame; + Drawable(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ @@ -230,7 +234,7 @@ class SG_EXPORT Drawable : public Object inline bool getUseDisplayList() const { return _useDisplayList; } /** When set to true, ignore the setUseDisplayList() settings, and hints to the drawImplementation - method to use OpenGL vertex buffer objects for rendering..*/ + method to use OpenGL vertex buffer objects for rendering.*/ void setUseVertexBufferObjects(bool flag); /** Return whether OpenGL vertex buffer objects should be used when supported by OpenGL driver.*/ @@ -241,6 +245,11 @@ class SG_EXPORT Drawable : public Object void dirtyDisplayList(); + /** Return the estimated size of GLObjects (display lists/vertex buffer objects) that are associated with this drawable. + * This size is used a hint for reuse of deleteed display lists/vertex buffer objects. */ + virtual unsigned int getGLObjectSizeHint() const { return 0; } + + /** draw OpenGL primitives. * If the drawable has _useDisplayList set to true then use an OpenGL display @@ -343,12 +352,13 @@ class SG_EXPORT Drawable : public Object virtual void drawImplementation(State& state) const = 0; + static GLuint generateDisplayList(unsigned int contextID, unsigned int sizeHint = 0); /** use deleteDisplayList instead of glDeleteList to allow * OpenGL display list to be cached until they can be deleted * by the OpenGL context in which they were created, specified * by contextID.*/ - static void deleteDisplayList(unsigned int contextID,GLuint globj); + static void deleteDisplayList(unsigned int contextID,GLuint globj, unsigned int sizeHint = 0); /** flush all the cached display list which need to be deleted * in the OpenGL context related to contextID.*/ @@ -754,7 +764,7 @@ inline void Drawable::draw(State& state) const else if (_useDisplayList) { #ifdef USE_SEPARATE_COMPILE_AND_EXECUTE - globj = glGenLists( 1 ); + globj = generateDisplayList(contextID, getGLObjectSizeHint()); // glGenLists( 1 ); glNewList( globj, GL_COMPILE ); if (_drawCallback.valid()) _drawCallback->drawImplementation(state,this); @@ -764,7 +774,7 @@ inline void Drawable::draw(State& state) const glCallList( globj); #else - globj = glGenLists( 1 ); + globj = generateDisplayList(contextID, getGLObjectSizeHint()); // glGenLists( 1 ); glNewList( globj, GL_COMPILE_AND_EXECUTE ); if (_drawCallback.valid()) _drawCallback->drawImplementation(state,this); diff --git a/include/osg/Geometry b/include/osg/Geometry index 2c5636b1f..04de57c3a 100644 --- a/include/osg/Geometry +++ b/include/osg/Geometry @@ -330,6 +330,10 @@ class SG_EXPORT Geometry : public Drawable const osg::Geometry* getInternalOptimizedGeometry() const { return _internalOptimizedGeometry.get(); } + /** Return the estimated size of GLObjects (display lists/vertex buffer objects) that are associated with this drawable. + * This size is used a hint for reuse of deleteed display lists/vertex buffer objects. */ + virtual unsigned int getGLObjectSizeHint() const; + /** Draw Geometry directly ignoring an OpenGL display list which could be attached. * This is the internal draw method which does the drawing itself, * and is the method to override when deriving from Geometry for user-drawn objects. diff --git a/include/osg/Group b/include/osg/Group index 78f5fa743..d88058084 100644 --- a/include/osg/Group +++ b/include/osg/Group @@ -71,7 +71,7 @@ class SG_EXPORT Group : public Node /** Replace specified Node with another Node. * Equivalent to setChild(getChildIndex(orignChild),node) * See docs for setChild for futher details on implementation. - */ + */ virtual bool replaceChild( Node *origChild, Node* newChild ); /** Return the number of chilren nodes. */ diff --git a/include/osg/PagedLOD b/include/osg/PagedLOD index b3d996a65..b97097897 100644 --- a/include/osg/PagedLOD +++ b/include/osg/PagedLOD @@ -77,6 +77,15 @@ class SG_EXPORT PagedLOD : public LOD unsigned int getNumTimeStamps() const { return _perRangeDataList.size(); } + /** Set the frame number of the last time that this PageLOD node was traversed. + * Note, this frame number is automatically set by the traverse() method for all traversals (update, cull etc.). + */ + inline void setFrameNumberOfLastTraversal(int frameNumber) { _frameNumberOfLastTraversal=frameNumber; } + + /** Get the frame number of the last time that this PageLOD node was traversed.*/ + inline int getFrameNumberOfLastTraversal() const { return _frameNumberOfLastTraversal; } + + /** Set the number of children that the PagedLOD must keep around, even if they are older than their expiry time.*/ inline void setNumChildrenThatCannotBeExpired(unsigned int num) { _numChildrenThatCannotBeExpired = num; } @@ -84,9 +93,12 @@ class SG_EXPORT PagedLOD : public LOD unsigned int getNumChildrenThatCannotBeExpired() const { return _numChildrenThatCannotBeExpired; } /** Remove the children from the PagedLOD which haven't been visited since specified expiry time. - The removed children are added to the removeChildren list passed into the method, - this allows the children to be deleted later at the caller's discretion.*/ - virtual void removeExpiredChildren(double expiryTime,NodeList& removedChildren); + * The removed children are added to the removeChildren list passed into the method, + * this allows the children to be deleted later at the caller's discretion. + * Return true if children are removed, false otherwise. */ + bool removeExpiredChildren(double expiryTime,NodeList& removedChildren); + + protected : @@ -100,8 +112,8 @@ class SG_EXPORT PagedLOD : public LOD void expandPerRangeDataTo(unsigned int pos); - unsigned int _numChildrenThatCannotBeExpired; - + int _frameNumberOfLastTraversal; + unsigned int _numChildrenThatCannotBeExpired; PerRangeDataList _perRangeDataList; }; diff --git a/include/osg/PrimitiveSet b/include/osg/PrimitiveSet index 536b9159d..0d5a874b5 100644 --- a/include/osg/PrimitiveSet +++ b/include/osg/PrimitiveSet @@ -333,7 +333,7 @@ class SG_EXPORT DrawElementsUByte : public PrimitiveSet, public VectorUByte typedef osg::buffered_value GLObjectList; mutable GLObjectList _vboList; - virtual ~DrawElementsUByte() {} + virtual ~DrawElementsUByte(); }; @@ -381,7 +381,7 @@ class SG_EXPORT DrawElementsUShort : public PrimitiveSet, public VectorUShort typedef osg::buffered_value GLObjectList; mutable GLObjectList _vboList; - virtual ~DrawElementsUShort() {} + virtual ~DrawElementsUShort(); }; class SG_EXPORT DrawElementsUInt : public PrimitiveSet, public VectorUInt @@ -425,10 +425,10 @@ class SG_EXPORT DrawElementsUInt : public PrimitiveSet, public VectorUInt protected: + virtual ~DrawElementsUInt(); + typedef osg::buffered_value GLObjectList; mutable GLObjectList _vboList; - - virtual ~DrawElementsUInt() {} }; } diff --git a/include/osg/Texture b/include/osg/Texture index 633f3c322..d9686447f 100644 --- a/include/osg/Texture +++ b/include/osg/Texture @@ -162,6 +162,7 @@ namespace osg { + /** Texture pure virtual base class that encapsulates OpenGl texture * functionality common to the various types of OSG textures. */ @@ -170,6 +171,10 @@ class SG_EXPORT Texture : public osg::StateAttribute public : + static unsigned int s_numberTextureReusedLastInLastFrame; + static unsigned int s_numberNewTextureInLastFrame; + static unsigned int s_numberDeletedTextureInLastFrame; + Texture(); /** Copy constructor using CopyOp to manage deep vs shallow copy. */ diff --git a/include/osgDB/DatabasePager b/include/osgDB/DatabasePager index d6a087e02..27b890a58 100644 --- a/include/osgDB/DatabasePager +++ b/include/osgDB/DatabasePager @@ -28,7 +28,7 @@ #include #include -#include +#include namespace osgDB { @@ -211,7 +211,7 @@ class OSGDB_EXPORT DatabasePager : public osg::NodeVisitor::DatabaseRequestHandl public: - typedef std::set< osg::ref_ptr > PagedLODList; + typedef std::list< osg::ref_ptr > PagedLODList; typedef std::vector< osg::ref_ptr > StateSetList; typedef std::vector< osg::ref_ptr > DrawableList; @@ -288,7 +288,8 @@ class OSGDB_EXPORT DatabasePager : public osg::NodeVisitor::DatabaseRequestHandl OpenThreads::Mutex _dataToMergeListMutex; - PagedLODList _pagedLODList; + PagedLODList _activePagedLODList; + PagedLODList _inactivePagedLODList; double _expiryDelay; diff --git a/src/osg/Drawable.cpp b/src/osg/Drawable.cpp index a7d720301..be0832644 100644 --- a/src/osg/Drawable.cpp +++ b/src/osg/Drawable.cpp @@ -33,6 +33,9 @@ using namespace osg; +unsigned int Drawable::s_numberNewDrawablesInLastFrame = 0; +unsigned int Drawable::s_numberDeletedDrawablesInLastFrame = 0; + // static cache of deleted display lists which can only // by completely deleted once the appropriate OpenGL context // is set. Used osg::Drawable::deleteDisplayList(..) and flushDeletedDisplayLists(..) below. @@ -45,7 +48,23 @@ typedef std::map DeletedDisplayListCache; static DeletedDisplayListCache s_deletedDisplayListCache; -void Drawable::deleteDisplayList(unsigned int contextID,GLuint globj) +GLuint Drawable::generateDisplayList(unsigned int contextID, unsigned int sizeHint) +{ +#ifdef THREAD_SAFE_GLOBJECT_DELETE_LISTS + OpenThreads::ScopedLock lock(s_mutex_deletedDisplayListCache); +#endif + DisplayListList& dll = s_deletedDisplayListCache[contextID]; + if (dll.empty()) return glGenLists( 1 ); + else + { + notify(NOTICE)<<"reusing display list "<getNumElements() : 0; +} + void Geometry::drawImplementation(State& state) const { if (_internalOptimizedGeometry.valid()) diff --git a/src/osg/PagedLOD.cpp b/src/osg/PagedLOD.cpp index bca8e58e1..f91b1b2c8 100644 --- a/src/osg/PagedLOD.cpp +++ b/src/osg/PagedLOD.cpp @@ -25,6 +25,7 @@ PagedLOD::PerRangeData& PagedLOD::PerRangeData::operator = (const PerRangeData& PagedLOD::PagedLOD() { + _frameNumberOfLastTraversal = 0; _centerMode = USER_DEFINED_CENTER; _radius = -1; _numChildrenThatCannotBeExpired = 0; @@ -32,6 +33,7 @@ PagedLOD::PagedLOD() PagedLOD::PagedLOD(const PagedLOD& plod,const CopyOp& copyop): LOD(plod,copyop), + _frameNumberOfLastTraversal(plod._frameNumberOfLastTraversal), _numChildrenThatCannotBeExpired(plod._numChildrenThatCannotBeExpired), _perRangeDataList(plod._perRangeDataList) { @@ -40,6 +42,9 @@ PagedLOD::PagedLOD(const PagedLOD& plod,const CopyOp& copyop): void PagedLOD::traverse(NodeVisitor& nv) { + // set the frame number of the traversal so that external nodes can find out how active this + // node is. + if (nv.getFrameStamp()) setFrameNumberOfLastTraversal(nv.getFrameStamp()->getFrameNumber()); double timeStamp = nv.getFrameStamp()?nv.getFrameStamp()->getReferenceTime():0.0; bool updateTimeStamp = nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR; @@ -179,7 +184,7 @@ bool PagedLOD::removeChild( Node *child ) return Group::removeChild(child); } -void PagedLOD::removeExpiredChildren(double expiryTime,NodeList& removedChildren) +bool PagedLOD::removeExpiredChildren(double expiryTime,NodeList& removedChildren) { if (_children.size()>_numChildrenThatCannotBeExpired) { @@ -187,7 +192,8 @@ void PagedLOD::removeExpiredChildren(double expiryTime,NodeList& removedChildren { osg::Node* nodeToRemove = _children[_children.size()-1].get(); removedChildren.push_back(nodeToRemove); - Group::removeChild(nodeToRemove); + return Group::removeChild(nodeToRemove); } } + return false; } diff --git a/src/osg/PrimitiveSet.cpp b/src/osg/PrimitiveSet.cpp index 0ac70f527..31fc66d0b 100644 --- a/src/osg/PrimitiveSet.cpp +++ b/src/osg/PrimitiveSet.cpp @@ -77,6 +77,18 @@ unsigned int DrawArrayLengths::getNumIndices() const return count; } +DrawElementsUByte::~DrawElementsUByte() +{ + for(unsigned int i=0;i<_vboList.size();++i) + { + if (_vboList[i] != 0) + { + Drawable::deleteVertexBufferObject(i,_vboList[i]); + _vboList[i] = 0; + } + } +} + void DrawElementsUByte::draw(State& state, bool useVertexBufferObjects) const { if (useVertexBufferObjects) @@ -125,6 +137,18 @@ void DrawElementsUByte::offsetIndices(int offset) } +DrawElementsUShort::~DrawElementsUShort() +{ + for(unsigned int i=0;i<_vboList.size();++i) + { + if (_vboList[i] != 0) + { + Drawable::deleteVertexBufferObject(i,_vboList[i]); + _vboList[i] = 0; + } + } +} + void DrawElementsUShort::draw(State& state, bool useVertexBufferObjects) const { if (useVertexBufferObjects) @@ -173,6 +197,18 @@ void DrawElementsUShort::offsetIndices(int offset) } +DrawElementsUInt::~DrawElementsUInt() +{ + for(unsigned int i=0;i<_vboList.size();++i) + { + if (_vboList[i] != 0) + { + Drawable::deleteVertexBufferObject(i,_vboList[i]); + _vboList[i] = 0; + } + } +} + void DrawElementsUInt::draw(State& state, bool useVertexBufferObjects) const { if (useVertexBufferObjects) diff --git a/src/osg/Texture.cpp b/src/osg/Texture.cpp index ba845daea..6e984c62c 100644 --- a/src/osg/Texture.cpp +++ b/src/osg/Texture.cpp @@ -37,6 +37,10 @@ using namespace osg; #define GL_STORAGE_SHARED_APPLE 0x85BF #endif +unsigned int Texture::s_numberTextureReusedLastInLastFrame = 0; +unsigned int Texture::s_numberNewTextureInLastFrame = 0; +unsigned int Texture::s_numberDeletedTextureInLastFrame = 0; + Texture::TextureObject* Texture::TextureObjectManager::generateTextureObject(unsigned int /*contextID*/,GLenum target) { GLuint id; @@ -45,6 +49,8 @@ Texture::TextureObject* Texture::TextureObjectManager::generateTextureObject(uns return new Texture::TextureObject(id,target); } +static int s_number = 0; + Texture::TextureObject* Texture::TextureObjectManager::generateTextureObject(unsigned int /*contextID*/, GLenum target, GLint numMipmapLevels, @@ -54,6 +60,10 @@ Texture::TextureObject* Texture::TextureObjectManager::generateTextureObject(uns GLsizei depth, GLint border) { + ++s_number; + ++s_numberNewTextureInLastFrame; + // notify(NOTICE)<<"creating new texture object "< lock(_mutex); @@ -151,16 +166,19 @@ void Texture::TextureObjectManager::flushTextureObjects(unsigned int contextID,d double expiryTime = currentTime-_expiryDelay; - unsigned int numTexturesDeleted = 0; for(itr=tol.begin(); - itr!=tol.end() && elapsedTime_timeStamp_id)); itr = tol.erase(itr); ++numTexturesDeleted; + ++numObjectsDeleted; } else { @@ -169,9 +187,10 @@ void Texture::TextureObjectManager::flushTextureObjects(unsigned int contextID,d elapsedTime = timer.delta_s(start_tick,timer.tick()); } - if (numTexturesDeleted) notify(osg::INFO)<<"Number of Texture's deleted = "< +#include #include #include @@ -110,7 +110,8 @@ void DatabasePager::clear() } // no mutex?? - _pagedLODList.clear(); + _activePagedLODList.clear(); + _inactivePagedLODList.clear(); // ?? // _activeGraphicsContexts @@ -312,6 +313,9 @@ public: inline void apply(osg::Drawable* drawable) { apply(drawable->getStateSet()); + + // drawable->setUseDisplayList(false); + // drawable->setUseVertexBufferObjects(true); if (drawable->getUseDisplayList() || drawable->getUseVertexBufferObjects()) { @@ -351,7 +355,7 @@ void DatabasePager::run() // need to set the texture object manager to be able to reuse textures // by keeping deleted texture objects around for 10 seconds after being deleted. - osg::Texture::getTextureObjectManager()->setExpiryDelay(10.0f); + osg::Texture::getTextureObjectManager()->setExpiryDelay(30.0f); bool firstTime = true; @@ -498,12 +502,41 @@ void DatabasePager::run() firstTime = false; } - } while (!testCancel()); + } while (!testCancel() && !_done); } void DatabasePager::addLoadedDataToSceneGraph(double timeStamp) { + static double s_previous = timeStamp; + double timeDelta = timeStamp-s_previous; + + + if (timeDelta>0.02) + { + std::string str; + if (osg::Texture::s_numberTextureReusedLastInLastFrame > 0) str += " RT "; + if (osg::Texture::s_numberNewTextureInLastFrame > 0) str += " NT "; + if (osg::Texture::s_numberDeletedTextureInLastFrame > 0) str += " DT "; + if (osg::Drawable::s_numberNewDrawablesInLastFrame > 0) str += " ND "; + if (osg::Drawable::s_numberDeletedDrawablesInLastFrame > 0) str += " DD "; + + if (str.empty()) str += " ?? "; + + osg::notify(osg::NOTICE)<<"addLoadedDataToSceneGraph "<_fileName<<" after "<_numOfRequests<<" requests."<delta_m(before,osg::Timer::instance()->tick())<<" ms objects"<tick(); double expiryTime = currentFrameTime - _expiryDelay; osg::NodeList childrenRemoved; + - //osg::notify(osg::INFO)<<"DatabasePager::removeExpiredSubgraphs("<get(); - const_cast(plod)->removeExpiredChildren(expiryTime,childrenRemoved); + const osg::PagedLOD* plod = active_itr->get(); + bool remove_plod = false; + if (plod->referenceCount()<=1) + { + // prune PageLOD's that are no longer externally referenced + childrenRemoved.push_back(const_cast(plod)); + //osg::notify(osg::NOTICE)<<"_activePagedLODList : pruning no longer externally referenced"<getFrameNumberOfLastTraversal()<_frameNumber) + { + // osg::notify(osg::NOTICE)<<"_activePagedLODList : moving PageLOD to inactive list"<targetNumOfInActivePagedLODs) targetNumOfRemovedChildPagedLODs = _inactivePagedLODList.size()-targetNumOfInActivePagedLODs; + + if (targetNumOfRemovedChildPagedLODs>1) targetNumOfRemovedChildPagedLODs=1; + + + // filter out singly referenced PagedLOD and move reactivated PagedLOD into the active list + for(PagedLODList::iterator inactive_itr = _inactivePagedLODList.begin(); + inactive_itr!=_inactivePagedLODList.end(); + ) + { + const osg::PagedLOD* plod = inactive_itr->get(); + bool remove_plod = false; + if (plod->referenceCount()<=1) + { + // prune PageLOD's that are no longer externally referenced + childrenRemoved.push_back(const_cast(plod)); + //osg::notify(osg::NOTICE)<<"_activePagedLODList : pruning no longer externally referenced"<getFrameNumberOfLastTraversal()>=_frameNumber) + { + // osg::notify(osg::NOTICE)<<"_inactivePagedLODList : moving PageLOD to active list"<(plod)->removeExpiredChildren(currentFrameTime,childrenRemoved)) +// if (const_cast(plod)->removeExpiredChildren(expiryTime,childrenRemoved)) + { + //osg::notify(osg::NOTICE)<<"Some children removed from PLod"<delta_m(before,osg::Timer::instance()->tick()); + //osg::notify(osg::NOTICE)<<" time 1 "<accept(cuplv); - } - // pass the objects across to the database pager delete list { OpenThreads::ScopedLock lock(_childrenToDeleteListMutex); @@ -604,6 +710,7 @@ void DatabasePager::removeExpiredSubgraphs(double currentFrameTime) childrenRemoved.clear(); } + // osg::notify(osg::NOTICE)<<" time 2 "<delta_m(before,osg::Timer::instance()->tick())<<" ms "<getSharedStateManager()) @@ -612,6 +719,10 @@ void DatabasePager::removeExpiredSubgraphs(double currentFrameTime) // update the Registry object cache. osgDB::Registry::instance()->updateTimeStampOfObjectsInCacheWithExtenalReferences(currentFrameTime); osgDB::Registry::instance()->removeExpiredObjectsInCache(expiryTime); + + + // osg::notify(osg::NOTICE)<<"Done DatabasePager::removeExpiredSubgraphs() "<delta_m(before,osg::Timer::instance()->tick())<<" ms "<accept(fplv); } @@ -660,6 +771,7 @@ bool DatabasePager::getCompileGLObjectsForContexID(unsigned int contextID) void DatabasePager::compileGLObjects(osg::State& state, double& availableTime) { +// osg::notify(osg::NOTICE)<<"DatabasePager::compileGLObjects "<<_frameNumber< lock(_childrenToDeleteListMutex); + + for(DatabaseRequestList::iterator ditr=litr; + ditr!=_dataToCompileList.end(); + ++ditr) + { + _childrenToDeleteList.push_back((*ditr)->_loadedModel.get()); + } + } //osg::notify(osg::NOTICE)<<"Pruning "<<_dataToCompileList.size()-i<_dataToCompileMap; DataToCompile& dtc = dcm[state.getContextID()]; - if (!dtc.first.empty() && (elapsedTime+estimatedTextureDuration)