/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace osg; // static cache of deleted buffer object lists which can only // by completely deleted once the appropriate OpenGL context // is set. Used osg::BufferObject::deleteBufferObject(..) and flushDeletedBufferObjects(..) below. typedef std::multimap BufferObjectMap; typedef osg::buffered_object DeletedBufferObjectCache; static OpenThreads::Mutex s_mutex_deletedBufferObjectCache; static DeletedBufferObjectCache s_deletedBufferObjectCache; unsigned int s_minimumNumberOfGLBufferObjectsToRetainInCache = 1000; ////////////////////////////////////////////////////////////////////////////////////////////////////// // // GLBufferObject // GLBufferObject::GLBufferObject(unsigned int contextID, BufferObject* bufferObject): _contextID(contextID), _glObjectID(0), _profile(0,0,0), _allocatedSize(0), _dirty(true), _bufferObject(0), _set(0), _previous(0), _next(0), _extensions(0) { assign(bufferObject); _extensions = GLBufferObject::getExtensions(contextID, true); _extensions->glGenBuffers(1, &_glObjectID); } GLBufferObject::~GLBufferObject() { } void GLBufferObject::bindBuffer() { _extensions->glBindBuffer(_profile._target,_glObjectID); if (_set) _set->moveToBack(this); } void GLBufferObject::setBufferObject(BufferObject* bufferObject) { assign(bufferObject); } void GLBufferObject::assign(BufferObject* bufferObject) { _bufferObject = bufferObject; if (_bufferObject) { _profile = bufferObject->getProfile(); _dirty = true; _bufferEntries.clear(); } else { _profile.setProfile(0,0,0); // clear all previous entries; _bufferEntries.clear(); } } void GLBufferObject::clear() { _bufferEntries.clear(); _dirty = true; } void GLBufferObject::compileBuffer() { _dirty = false; _bufferEntries.reserve(_bufferObject->getNumBufferData()); bool compileAll = false; bool offsetChanged = false; GLsizeiptrARB newTotalSize = 0; unsigned int i=0; for(; i<_bufferObject->getNumBufferData(); ++i) { BufferData* bd = _bufferObject->getBufferData(i); if (i<_bufferEntries.size()) { BufferEntry& entry = _bufferEntries[i]; if (offsetChanged || entry.dataSource != bd || entry.dataSize != bd->getTotalDataSize()) { GLsizeiptrARB previousEndOfBufferDataMarker = GLsizeiptrARB(entry.offset) + entry.dataSize; // osg::notify(osg::NOTICE)<<"GLBufferObject::compileBuffer(..) updating BufferEntry"<getTotalDataSize(); entry.dataSource = bd; newTotalSize += entry.dataSize; if (previousEndOfBufferDataMarker==newTotalSize) { offsetChanged = true; } } } else { BufferEntry entry; entry.offset = newTotalSize; entry.modifiedCount = 0xffffff; entry.dataSize = bd->getTotalDataSize(); entry.dataSource = bd; #if 0 osg::notify(osg::NOTICE)<<"entry"<glBindBuffer(_profile._target, _glObjectID); if (newTotalSize > _profile._size) { osg::notify(osg::NOTICE)<<"newTotalSize="<getModifiedCount(); if (vboMemory) memcpy(vboMemory + (GLsizeiptrARB)entry.offset, entry.dataSource->getDataPointer(), entry.dataSize); else _extensions->glBufferSubData(_profile._target, (GLintptrARB)entry.offset, (GLsizeiptrARB)entry.dataSize, entry.dataSource->getDataPointer()); } } // Unmap the texture image buffer if (vboMemory) _extensions->glUnmapBuffer(_profile._target); } void GLBufferObject::deleteGLObject() { if (_glObjectID!=0) { _extensions->glDeleteBuffers(1, &_glObjectID); _glObjectID = 0; _allocatedSize = 0; _bufferEntries.clear(); } } void GLBufferObject::deleteBufferObject(unsigned int contextID,GLuint globj) { osg::notify(osg::NOTICE)<<"GLBufferObject::deleteBufferObject("< lock(s_mutex_deletedBufferObjectCache); const Extensions* extensions = getExtensions(contextID,true); unsigned int noDeleted = 0; BufferObjectMap& dll = s_deletedBufferObjectCache[contextID]; BufferObjectMap::iterator ditr=dll.begin(); for(; ditr!=dll.end() && elapsedTimeglDeleteBuffers(1,&(ditr->second)); elapsedTime = timer.delta_s(start_tick,timer.tick()); ++noDeleted; } if (ditr!=dll.begin()) dll.erase(dll.begin(),ditr); // if (noDeleted!=0) notify(osg::NOTICE)<<"Number VBOs deleted = "< lock(s_mutex_deletedBufferObjectCache); BufferObjectMap& dll = s_deletedBufferObjectCache[contextID]; dll.clear(); } #endif ////////////////////////////////////////////////////////////////////////////// // // Extension support // typedef buffered_value< ref_ptr > BufferedExtensions; static BufferedExtensions s_extensions; GLBufferObject::Extensions* GLBufferObject::getExtensions(unsigned int contextID,bool createIfNotInitalized) { if (!s_extensions[contextID] && createIfNotInitalized) s_extensions[contextID] = new GLBufferObject::Extensions(contextID); return s_extensions[contextID].get(); } void GLBufferObject::setExtensions(unsigned int contextID,Extensions* extensions) { s_extensions[contextID] = extensions; } GLBufferObject::Extensions::Extensions(unsigned int contextID) { setupGLExtensions(contextID); } GLBufferObject::Extensions::Extensions(const Extensions& rhs): Referenced() { _glGenBuffers = rhs._glGenBuffers; _glBindBuffer = rhs._glBindBuffer; _glBufferData = rhs._glBufferData; _glBufferSubData = rhs._glBufferSubData; _glDeleteBuffers = rhs._glDeleteBuffers; _glIsBuffer = rhs._glIsBuffer; _glGetBufferSubData = rhs._glGetBufferSubData; _glMapBuffer = rhs._glMapBuffer; _glUnmapBuffer = rhs._glUnmapBuffer; _glGetBufferParameteriv = rhs._glGetBufferParameteriv; _glGetBufferPointerv = rhs._glGetBufferPointerv; } void GLBufferObject::Extensions::lowestCommonDenominator(const Extensions& rhs) { if (!rhs._glGenBuffers) _glGenBuffers = rhs._glGenBuffers; if (!rhs._glBindBuffer) _glBindBuffer = rhs._glBindBuffer; if (!rhs._glBufferData) _glBufferData = rhs._glBufferData; if (!rhs._glBufferSubData) _glBufferSubData = rhs._glBufferSubData; if (!rhs._glDeleteBuffers) _glDeleteBuffers = rhs._glDeleteBuffers; if (!rhs._glIsBuffer) _glIsBuffer = rhs._glIsBuffer; if (!rhs._glGetBufferSubData) _glGetBufferSubData = rhs._glGetBufferSubData; if (!rhs._glMapBuffer) _glMapBuffer = rhs._glMapBuffer; if (!rhs._glUnmapBuffer) _glUnmapBuffer = rhs._glUnmapBuffer; if (!rhs._glGetBufferParameteriv) _glGetBufferParameteriv = rhs._glGetBufferParameteriv; if (!rhs._glGetBufferParameteriv) _glGetBufferPointerv = rhs._glGetBufferPointerv; } void GLBufferObject::Extensions::setupGLExtensions(unsigned int contextID) { setGLExtensionFuncPtr(_glGenBuffers, "glGenBuffers","glGenBuffersARB"); setGLExtensionFuncPtr(_glBindBuffer, "glBindBuffer","glBindBufferARB"); setGLExtensionFuncPtr(_glBufferData, "glBufferData","glBufferDataARB"); setGLExtensionFuncPtr(_glBufferSubData, "glBufferSubData","glBufferSubDataARB"); setGLExtensionFuncPtr(_glDeleteBuffers, "glDeleteBuffers","glDeleteBuffersARB"); setGLExtensionFuncPtr(_glIsBuffer, "glIsBuffer","glIsBufferARB"); setGLExtensionFuncPtr(_glGetBufferSubData, "glGetBufferSubData","glGetBufferSubDataARB"); setGLExtensionFuncPtr(_glMapBuffer, "glMapBuffer","glMapBufferARB"); setGLExtensionFuncPtr(_glUnmapBuffer, "glUnmapBuffer","glUnmapBufferARB"); setGLExtensionFuncPtr(_glGetBufferParameteriv, "glGetBufferParameteriv","glGetBufferParameterivARB"); setGLExtensionFuncPtr(_glGetBufferPointerv, "glGetBufferPointerv","glGetBufferPointervARB"); _isPBOSupported = osg::isGLExtensionSupported(contextID,"GL_ARB_pixel_buffer_object"); } void GLBufferObject::Extensions::glGenBuffers(GLsizei n, GLuint *buffers) const { if (_glGenBuffers) _glGenBuffers(n, buffers); else notify(WARN)<<"Error: glGenBuffers not supported by OpenGL driver"<getContextID()), _profile(profile), _numOfGLBufferObjects(0), _head(0), _tail(0) { osg::notify(osg::NOTICE)<<"GLBufferObjectSet::GLBufferObjectSet _profile._size="<<_profile._size<_next) { if ((to->_next)->_previous != to) { osg::notify(osg::NOTICE)<<"Error (to->_next)->_previous != to "<_next)->_previous != to "; } } else { if (_tail != to) { osg::notify(osg::NOTICE)<<"Error _trail != to"<_next; } unsigned int totalNumber = numInList + _orphanedGLBufferObjects.size(); if (totalNumber != _numOfGLBufferObjects) { osg::notify(osg::NOTICE)<<"Error numInList + _orphanedGLBufferObjects.size() != _numOfGLBufferObjects"<get(); _orphanedGLBufferObjects.push_back(to); remove(to); #if 0 osg::notify(osg::NOTICE)<<" HPOTO after _head = "<<_head<_previous = "<_previous<_next = "<_next<getNumberOrphanedGLBufferObjects() += numOrphaned; _parent->getNumberActiveGLBufferObjects() -= numOrphaned; _pendingOrphanedGLBufferObjects.clear(); checkConsistency(); } void GLBufferObjectSet::flushAllDeletedGLBufferObjects() { for(GLBufferObjectList::iterator itr = _orphanedGLBufferObjects.begin(); itr != _orphanedGLBufferObjects.end(); ++itr) { (*itr)->deleteGLObject(); osg::notify(osg::NOTICE)<<"Deleting textureobject id="<<(*itr)->getGLObjectID()<getCurrGLBufferObjectPoolSize() -= numDeleted*_profile._size; _parent->getNumberOrphanedGLBufferObjects() -= numDeleted; _parent->getNumberDeleted() += numDeleted; _orphanedGLBufferObjects.clear(); } void GLBufferObjectSet::discardAllDeletedGLBufferObjects() { unsigned int numDiscarded = _orphanedGLBufferObjects.size(); _numOfGLBufferObjects -= numDiscarded; // update the GLBufferObjectManager's running total of current pool size _parent->setCurrGLBufferObjectPoolSize( _parent->getCurrGLBufferObjectPoolSize() - numDiscarded*_profile._size ); // update the number of active and orphaned TextureOjects _parent->getNumberOrphanedGLBufferObjects() -= 1; _parent->getNumberActiveGLBufferObjects() += 1; _parent->getNumberDeleted() += 1; // just clear the list as there is nothing else we can do with them when discarding them _orphanedGLBufferObjects.clear(); } void GLBufferObjectSet::flushDeletedGLBufferObjects(double currentTime, double& availableTime) { // if nothing to delete return if (_orphanedGLBufferObjects.empty()) return; // if no time available don't try to flush objects. if (availableTime<=0.0) return; // if we don't have too many orphaned texture objects then don't bother deleting them, as we can potentially reuse them later. if (_parent->getNumberOrphanedGLBufferObjects()<=s_minimumNumberOfGLBufferObjectsToRetainInCache) return; unsigned int numDeleted = 0; unsigned int maxNumObjectsToDelete = _parent->getNumberOrphanedGLBufferObjects()-s_minimumNumberOfGLBufferObjectsToRetainInCache; if (maxNumObjectsToDelete>4) maxNumObjectsToDelete = 4; ElapsedTime timer; GLBufferObjectList::iterator itr = _orphanedGLBufferObjects.begin(); for(; itr != _orphanedGLBufferObjects.end() && timer.elapsedTime()setBufferObject(bufferObject); glbo->setProfile(_profile); return glbo.release(); } // GLBufferObject* glbo = new GLBufferObject(_contextID, const_cast(bufferObject)); glbo->setProfile(_profile); glbo->_set = this; ++_numOfGLBufferObjects; // update the current texture pool size _parent->getCurrGLBufferObjectPoolSize() += _profile._size; _parent->getNumberActiveGLBufferObjects() += 1; addToBack(glbo); // osg::notify(osg::NOTICE)<<"Created new GLBufferObject, _numOfGLBufferObjects "<<_numOfGLBufferObjects<_previous = "<_previous<_next = "<_next<_frameLastUsed = _parent->getFrameNumber(); // nothing to do if we are already tail if (to==_tail) return; // if no tail exists then assign 'to' as tail and head if (_tail==0) { osg::notify(osg::NOTICE)<<"Error ***************** Should not get here !!!!!!!!!"<_next==0) { osg::notify(osg::NOTICE)<<"Error ***************** Should not get here either !!!!!!!!!"<_previous) { (to->_previous)->_next = to->_next; } else { // 'to' is the head, so moving it to the back will mean we need a new head if (to->_next) { _head = to->_next; } } (to->_next)->_previous = to->_previous; _tail->_next = to; to->_previous = _tail; to->_next = 0; _tail = to; #if 0 osg::notify(osg::NOTICE)<<" m2B after _head = "<<_head<_previous = "<_previous<_next = "<_next<_previous = "<_previous<_next = "<_next<_previous !=0 || to->_next !=0) { moveToBack(to); } else { to->_frameLastUsed = _parent->getFrameNumber(); if (_tail) _tail->_next = to; to->_previous = _tail; if (!_head) _head = to; _tail = to; } #if 0 osg::notify(osg::NOTICE)<<" a2B after _head = "<<_head<_previous = "<_previous<_next = "<_next< lock(_mutex); // disconnect from original texture to->setBufferObject(0); // add orphan 'to' to the pending list of orphans, these will then be // handled in the handlePendingOrphandedGLBufferObjects() where the TO's // will be removed from the active list, and then placed in the orhpanGLBufferObject // list. This double buffered approach to handling orphaned TO's is used // to avoid having to mutex the process of appling active TO's. _pendingOrphanedGLBufferObjects.push_back(to); #if 0 osg::notify(osg::NOTICE)<<"GLBufferObjectSet::orphan("<getFrameNumber(); else ++_frameNumber; ++_numFrames; } void GLBufferObjectManager::reportStats() { double numFrames(_numFrames==0 ? 1.0 : _numFrames); osg::notify(osg::NOTICE)<<"GLBufferObjectMananger::reportStats()"<& GLBufferObjectManager::getGLBufferObjectManager(unsigned int contextID) { typedef osg::buffered_object< ref_ptr > GLBufferObjectManagerBuffer; static GLBufferObjectManagerBuffer s_GLBufferObjectManager; if (!s_GLBufferObjectManager[contextID]) s_GLBufferObjectManager[contextID] = new GLBufferObjectManager(contextID); return s_GLBufferObjectManager[contextID]; } GLBufferObject* GLBufferObject::createGLBufferObject(unsigned int contextID, const BufferObject* bufferObject) { return GLBufferObjectManager::getGLBufferObjectManager(contextID)->generateGLBufferObject(bufferObject); } void GLBufferObject::flushAllDeletedBufferObjects(unsigned int contextID) { GLBufferObjectManager::getGLBufferObjectManager(contextID)->flushAllDeletedGLBufferObjects(); } void GLBufferObject::discardAllDeletedBufferObjects(unsigned int contextID) { GLBufferObjectManager::getGLBufferObjectManager(contextID)->discardAllDeletedGLBufferObjects(); } void GLBufferObject::flushDeletedBufferObjects(unsigned int contextID,double currentTime, double& availbleTime) { GLBufferObjectManager::getGLBufferObjectManager(contextID)->flushDeletedGLBufferObjects(currentTime, availbleTime); } void GLBufferObject::releaseGLBufferObject(unsigned int contextID, GLBufferObject* to) { GLBufferObjectManager::getGLBufferObjectManager(contextID)->releaseGLBufferObject(to); } ////////////////////////////////////////////////////////////////////////////////////////////////////// // // BufferObject // BufferObject::BufferObject() { } BufferObject::BufferObject(const BufferObject& bo,const CopyOp& copyop): Object(bo,copyop) { } BufferObject::~BufferObject() { releaseGLObjects(0); } void BufferObject::setBufferData(unsigned int index, BufferData* bd) { if (index>=_bufferDataList.size()) _bufferDataList.resize(index+1); _bufferDataList[index] = bd; } void BufferObject::dirty() { for(unsigned int i=0; i<_glBufferObjects.size(); ++i) { if (_glBufferObjects[i].valid()) _glBufferObjects[i]->dirty(); } } void BufferObject::resizeGLObjectBuffers(unsigned int maxSize) { _glBufferObjects.resize(maxSize); } void BufferObject::releaseGLObjects(State* state) const { if (state) { unsigned int contextID = state->getContextID(); if (_glBufferObjects[contextID].valid()) { GLBufferObject::releaseGLBufferObject(contextID, _glBufferObjects[contextID].get()); _glBufferObjects[contextID] = 0; } } else { for(unsigned int i=0; i<_glBufferObjects.size();++i) { if (_glBufferObjects[i].valid()) { GLBufferObject::releaseGLBufferObject(i, _glBufferObjects[i].get()); _glBufferObjects[i] = 0; } } } } unsigned int BufferObject::addBufferData(BufferData* bd) { if (!bd) return 0; // check to see if bd exists in BufferObject already, is so return without doing anything for(BufferDataList::iterator itr = _bufferDataList.begin(); itr != _bufferDataList.end(); ++itr) { if (*itr == bd) return bd->getBufferIndex(); } // bd->setBufferIndex(_bufferDataList.size()); _bufferDataList.push_back(bd); // osg::notify(osg::NOTICE)<<"BufferObject "<