/* -*-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 display lists which can only // by completely deleted once the appropriate OpenGL context // is set. Used osg::BufferObject::deleteDisplayList(..) and flushDeletedBufferObjects(..) below. typedef std::multimap DisplayListMap; typedef osg::buffered_object DeletedBufferObjectCache; static OpenThreads::Mutex s_mutex_deletedBufferObjectCache; static DeletedBufferObjectCache s_deletedBufferObjectCache; void BufferObject::deleteBufferObject(unsigned int contextID,GLuint globj) { if (globj!=0) { OpenThreads::ScopedLock lock(s_mutex_deletedBufferObjectCache); // insert the globj into the cache for the appropriate context. s_deletedBufferObjectCache[contextID].insert(DisplayListMap::value_type(0,globj)); } } /** flush all the cached display list which need to be deleted * in the OpenGL context related to contextID.*/ void BufferObject::flushDeletedBufferObjects(unsigned int contextID,double /*currentTime*/, double& availableTime) { // if no time available don't try to flush objects. if (availableTime<=0.0) return; const osg::Timer& timer = *osg::Timer::instance(); osg::Timer_t start_tick = timer.tick(); double elapsedTime = 0.0; { OpenThreads::ScopedLock lock(s_mutex_deletedBufferObjectCache); const Extensions* extensions = getExtensions(contextID,true); unsigned int noDeleted = 0; DisplayListMap& dll = s_deletedBufferObjectCache[contextID]; DisplayListMap::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::INFO)<<"Number VBOs deleted = "<getContextID(); if (_bufferObjectList[contextID]) { deleteBufferObject(contextID,_bufferObjectList[contextID]); _bufferObjectList[contextID] = 0; } } else { for(unsigned int contextID=0;contextID<_bufferObjectList.size();++contextID) { if (_bufferObjectList[contextID]) { deleteBufferObject(contextID,_bufferObjectList[contextID]); _bufferObjectList[contextID] = 0; } } } } ////////////////////////////////////////////////////////////////////////////// // // Extension support // typedef buffered_value< ref_ptr > BufferedExtensions; static BufferedExtensions s_extensions; BufferObject::Extensions* BufferObject::getExtensions(unsigned int contextID,bool createIfNotInitalized) { if (!s_extensions[contextID] && createIfNotInitalized) s_extensions[contextID] = new BufferObject::Extensions(contextID); return s_extensions[contextID].get(); } void BufferObject::setExtensions(unsigned int contextID,Extensions* extensions) { s_extensions[contextID] = extensions; } BufferObject::Extensions::Extensions(unsigned int contextID) { setupGLExtensions(contextID); } BufferObject::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 BufferObject::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 BufferObject::Extensions::setupGLExtensions(unsigned int contextID) { _glGenBuffers = ((GenBuffersProc)osg::getGLExtensionFuncPtr("glGenBuffers","glGenBuffersARB")); _glBindBuffer = ((BindBufferProc)osg::getGLExtensionFuncPtr("glBindBuffer","glBindBufferARB")); _glBufferData = ((BufferDataProc)osg::getGLExtensionFuncPtr("glBufferData","glBufferDataARB")); _glBufferSubData = ((BufferSubDataProc)osg::getGLExtensionFuncPtr("glBufferSubData","glBufferSubDataARB")); _glDeleteBuffers = ((DeleteBuffersProc)osg::getGLExtensionFuncPtr("glDeleteBuffers","glDeleteBuffersARB")); _glIsBuffer = ((IsBufferProc)osg::getGLExtensionFuncPtr("glIsBuffer","glIsBufferARB")); _glGetBufferSubData = ((GetBufferSubDataProc)osg::getGLExtensionFuncPtr("glGetBufferSubData","glGetBufferSubDataARB")); _glMapBuffer = ((MapBufferProc)osg::getGLExtensionFuncPtr("glMapBuffer","glMapBufferARB")); _glUnmapBuffer = ((UnmapBufferProc)osg::getGLExtensionFuncPtr("glUnmapBuffer","glUnmapBufferARB")); _glGetBufferParameteriv = ((GetBufferParameterivProc)osg::getGLExtensionFuncPtr("glGetBufferParameteriv","glGetBufferParameterivARB")); _glGetBufferPointerv = ((GetBufferPointervProc)osg::getGLExtensionFuncPtr("glGetBufferPointerv","glGetBufferPointervARB")); _isPBOSupported = osg::isGLExtensionSupported(contextID,"GL_ARB_pixel_buffer_object"); } void BufferObject::Extensions::glGenBuffers(GLsizei n, GLuint *buffers) const { if (_glGenBuffers) _glGenBuffers(n, buffers); else notify(WARN)<<"Error: glGenBuffers not supported by OpenGL driver"<second == array) break; } if (itr != _bufferEntryArrayPairs.end()) _bufferEntryArrayPairs.erase(itr); } void VertexBufferObject::setArray(unsigned int i, Array* array) { if (i+1>=_bufferEntryArrayPairs.size()) _bufferEntryArrayPairs.resize(i+1); _bufferEntryArrayPairs[i].second = array; _bufferEntryArrayPairs[i].first.modifiedCount.setAllElementsTo(0xffffffff); _bufferEntryArrayPairs[i].first.offset = 0; dirty(); } void VertexBufferObject::compileBuffer(State& state) const { unsigned int contextID = state.getContextID(); _compiledList[contextID] = 1; Extensions* extensions = getExtensions(contextID,true); // osg::notify(osg::NOTICE)<<"VertexBufferObject::compileBuffer frameNumber="<getFrameNumber()<getTotalDataSize(); } } bool copyAll = false; GLuint& vbo = buffer(contextID); if (vbo==0) { // building for the first time. _totalSize = totalSizeRequired; // don't generate buffer if size is zero. if (_totalSize==0) return; extensions->glGenBuffers(1, &vbo); extensions->glBindBuffer(_target, vbo); extensions->glBufferData(_target, _totalSize, NULL, _usage); copyAll = true; } else { extensions->glBindBuffer(_target, vbo); if (_totalSize != totalSizeRequired) { // resize vbo. _totalSize = totalSizeRequired; extensions->glBufferData(_target, _totalSize, NULL, _usage); copyAll = true; } } // osg::Timer_t start_tick = osg::Timer::instance()->tick(); void* vboMemory = 0; #if 0 vboMemory = extensions->glMapBuffer(_target, GL_WRITE_ONLY_ARB); #endif unsigned int offset = 0; for(BufferEntryArrayPairs::const_iterator itr = _bufferEntryArrayPairs.begin(); itr != _bufferEntryArrayPairs.end(); ++itr) { const BufferEntryArrayPair& bep = *itr; const Array* de = bep.second; if (de) { if (copyAll || bep.first.modifiedCount[contextID] != bep.second->getModifiedCount() || bep.first.dataSize != bep.second->getTotalDataSize()) { // copy data across bep.first.dataSize = bep.second->getTotalDataSize(); bep.first.modifiedCount[contextID] = de->getModifiedCount(); if (copyAll) { bep.first.offset = offset; de->setVertexBufferObjectOffset((GLvoid*)offset); offset += bep.first.dataSize; } // osg::notify(osg::NOTICE)<<" copying vertex buffer data "<getDataPointer(), bep.first.dataSize); else extensions->glBufferSubData(_target, bep.first.offset, bep.first.dataSize, de->getDataPointer()); } } } // Unmap the texture image buffer if (vboMemory) extensions->glUnmapBuffer(_target); // osg::notify(osg::NOTICE)<<"pbo _totalSize="<<_totalSize<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<first.modifiedCount.resize(maxSize); } } ////////////////////////////////////////////////////////////////////////////////// // // ElementBufferObject // ElementBufferObject::ElementBufferObject() { _target = GL_ELEMENT_ARRAY_BUFFER_ARB; _usage = GL_STATIC_DRAW_ARB; } ElementBufferObject::ElementBufferObject(const ElementBufferObject& vbo,const CopyOp& copyop): BufferObject(vbo,copyop) { } ElementBufferObject::~ElementBufferObject() { } unsigned int ElementBufferObject::addDrawElements(osg::DrawElements* drawElements) { unsigned int i = _bufferEntryDrawElementsPairs.size(); _bufferEntryDrawElementsPairs.resize(i+1); _bufferEntryDrawElementsPairs[i].second = drawElements; _bufferEntryDrawElementsPairs[i].first.modifiedCount.setAllElementsTo(0xffffffff); _bufferEntryDrawElementsPairs[i].first.dataSize = 0; return i; } void ElementBufferObject::removeDrawElements(osg::DrawElements* drawElements) { BufferEntryDrawElementsPairs::iterator itr; for(itr = _bufferEntryDrawElementsPairs.begin(); itr != _bufferEntryDrawElementsPairs.end(); ++itr) { if (itr->second == drawElements) break; } if (itr != _bufferEntryDrawElementsPairs.end()) _bufferEntryDrawElementsPairs.erase(itr); } void ElementBufferObject::setDrawElements(unsigned int i, DrawElements* drawElements) { if (i+1>=_bufferEntryDrawElementsPairs.size()) _bufferEntryDrawElementsPairs.resize(i+1); _bufferEntryDrawElementsPairs[i].second = drawElements; _bufferEntryDrawElementsPairs[i].first.modifiedCount.setAllElementsTo(0xffffffff); _bufferEntryDrawElementsPairs[i].first.dataSize = 0; } void ElementBufferObject::compileBuffer(State& state) const { unsigned int contextID = state.getContextID(); _compiledList[contextID] = 1; // osg::notify(osg::NOTICE)<<"ElementBufferObject::compile"<getTotalDataSize(); } } bool copyAll = false; GLuint& ebo = buffer(contextID); if (ebo==0) { // building for the first time. _totalSize = totalSizeRequired; // don't generate buffer if size is zero. if (_totalSize==0) return; extensions->glGenBuffers(1, &ebo); extensions->glBindBuffer(_target, ebo); extensions->glBufferData(_target, _totalSize, NULL, _usage); copyAll = true; } else { extensions->glBindBuffer(_target, ebo); if (_totalSize != totalSizeRequired) { // resize EBO. _totalSize = totalSizeRequired; extensions->glBufferData(_target, _totalSize, NULL, _usage); copyAll = true; } } // osg::Timer_t start_tick = osg::Timer::instance()->tick(); void* eboMemory = 0; #if 0 eboMemory = extensions->glMapBuffer(_target, GL_WRITE_ONLY_ARB); #endif unsigned int offset = 0; for(BufferEntryDrawElementsPairs::const_iterator itr = _bufferEntryDrawElementsPairs.begin(); itr != _bufferEntryDrawElementsPairs.end(); ++itr) { const BufferEntryDrawElementsPair& bep = *itr; const DrawElements* de = bep.second; if (de) { if (copyAll || bep.first.modifiedCount[contextID] != bep.second->getModifiedCount() || bep.first.dataSize != bep.second->getTotalDataSize()) { // copy data across bep.first.dataSize = bep.second->getTotalDataSize(); bep.first.modifiedCount[contextID] = de->getModifiedCount(); if (copyAll) { bep.first.offset = offset; de->setElementBufferObjectOffset((GLvoid*)offset); offset += bep.first.dataSize; } if (eboMemory) memcpy((char*)eboMemory + bep.first.offset, de->getDataPointer(), bep.first.dataSize); else extensions->glBufferSubData(_target, bep.first.offset, bep.first.dataSize, de->getDataPointer()); } } } // Unmap the texture image buffer if (eboMemory) extensions->glUnmapBuffer(_target); // osg::notify(osg::NOTICE)<<"pbo _totalSize="<<_totalSize<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<first.modifiedCount.resize(maxSize); } } ////////////////////////////////////////////////////////////////////////////////// // // PixelBufferObject // PixelBufferObject::PixelBufferObject(osg::Image* image): BufferObject() { _target = GL_PIXEL_UNPACK_BUFFER_ARB; _usage = GL_STREAM_DRAW_ARB; _bufferEntryImagePair.second = image; } /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ PixelBufferObject::PixelBufferObject(const PixelBufferObject& buffer,const CopyOp& copyop): BufferObject(buffer,copyop), _bufferEntryImagePair(buffer._bufferEntryImagePair) { } PixelBufferObject::~PixelBufferObject() { } void PixelBufferObject::setImage(osg::Image* image) { if (_bufferEntryImagePair.second == image) return; _bufferEntryImagePair.second = image; dirty(); } void PixelBufferObject::compileBuffer(State& state) const { unsigned int contextID = state.getContextID(); _compiledList[contextID] = 1; osg::Image* image = _bufferEntryImagePair.second; _bufferEntryImagePair.first.modifiedCount[contextID] = image->getModifiedCount(); if (!image->valid()) return; Extensions* extensions = getExtensions(contextID,true); GLuint& pbo = buffer(contextID); if (pbo==0) { // building for the first time. _totalSize = image->getTotalSizeInBytes(); // don't generate buffer if size is zero. if (_totalSize==0) return; extensions->glGenBuffers(1, &pbo); extensions->glBindBuffer(_target, pbo); extensions->glBufferData(_target, _totalSize, NULL, _usage); } else { extensions->glBindBuffer(_target, pbo); if (_totalSize != image->getTotalSizeInBytes()) { // resize PBO. _totalSize = image->getTotalSizeInBytes(); extensions->glBufferData(_target, _totalSize, NULL, _usage); } } // osg::Timer_t start_tick = osg::Timer::instance()->tick(); void* pboMemory = extensions->glMapBuffer(_target, GL_WRITE_ONLY_ARB); // copy data across memcpy(pboMemory, image->data(), _totalSize); // Unmap the texture image buffer extensions->glUnmapBuffer(_target); _bufferEntryImagePair.first.modifiedCount[contextID] = image->getModifiedCount(); // osg::notify(osg::NOTICE)<<"pbo _totalSize="<<_totalSize<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<