Files
OpenSceneGraph/src/osg/BufferObject.cpp
Robert Osfield eac3dc1963 From Paul Melis, "Here is a list of fixes to misspelled APIs. Unfortunately, more than one
of these are public APIs and therefore will break linkage to existing
shared libraries."

Note from Robert Osfield, updated wrappers.
2007-06-27 20:36:16 +00:00

722 lines
23 KiB
C++

/* -*-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 <stdio.h>
#include <math.h>
#include <float.h>
#include <osg/BufferObject>
#include <osg/Notify>
#include <osg/GLExtensions>
#include <osg/Timer>
#include <osg/Image>
#include <osg/State>
#include <osg/PrimitiveSet>
#include <osg/Array>
#include <OpenThreads/ScopedLock>
#include <OpenThreads/Mutex>
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<unsigned int,GLuint> DisplayListMap;
typedef osg::buffered_object<DisplayListMap> DeletedBufferObjectCache;
static OpenThreads::Mutex s_mutex_deletedBufferObjectCache;
static DeletedBufferObjectCache s_deletedBufferObjectCache;
void BufferObject::deleteBufferObject(unsigned int contextID,GLuint globj)
{
if (globj!=0)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> 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<OpenThreads::Mutex> 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() && elapsedTime<availableTime;
++ditr)
{
extensions->glDeleteBuffers(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 = "<<noDeleted<<std::endl;
}
availableTime -= elapsedTime;
}
BufferObject::BufferObject():
_target(0),
_usage(0),
_totalSize(0)
{
}
BufferObject::BufferObject(const BufferObject& bo,const CopyOp& copyop):
Object(bo,copyop)
{
}
BufferObject::~BufferObject()
{
releaseGLObjects(0);
}
void BufferObject::resizeGLObjectBuffers(unsigned int maxSize)
{
_bufferObjectList.resize(maxSize);
}
void BufferObject::releaseGLObjects(State* state) const
{
if (state)
{
unsigned int contextID = state->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<BufferObject::Extensions> > 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"<<std::endl;
}
void BufferObject::Extensions::glBindBuffer(GLenum target, GLuint buffer) const
{
if (_glBindBuffer) _glBindBuffer(target, buffer);
else notify(WARN)<<"Error: glBindBuffer not supported by OpenGL driver"<<std::endl;
}
void BufferObject::Extensions::glBufferData(GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage) const
{
if (_glBufferData) _glBufferData(target, size, data, usage);
else notify(WARN)<<"Error: glBufferData not supported by OpenGL driver"<<std::endl;
}
void BufferObject::Extensions::glBufferSubData(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data) const
{
if (_glBufferSubData) _glBufferSubData(target, offset, size, data);
else notify(WARN)<<"Error: glBufferData not supported by OpenGL driver"<<std::endl;
}
void BufferObject::Extensions::glDeleteBuffers(GLsizei n, const GLuint *buffers) const
{
if (_glDeleteBuffers) _glDeleteBuffers(n, buffers);
else notify(WARN)<<"Error: glBufferData not supported by OpenGL driver"<<std::endl;
}
GLboolean BufferObject::Extensions::glIsBuffer (GLuint buffer) const
{
if (_glIsBuffer) return _glIsBuffer(buffer);
else
{
notify(WARN)<<"Error: glIsBuffer not supported by OpenGL driver"<<std::endl;
return GL_FALSE;
}
}
void BufferObject::Extensions::glGetBufferSubData (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data) const
{
if (_glGetBufferSubData) _glGetBufferSubData(target,offset,size,data);
else notify(WARN)<<"Error: glGetBufferSubData not supported by OpenGL driver"<<std::endl;
}
GLvoid* BufferObject::Extensions::glMapBuffer (GLenum target, GLenum access) const
{
if (_glMapBuffer) return _glMapBuffer(target,access);
else
{
notify(WARN)<<"Error: glMapBuffer not supported by OpenGL driver"<<std::endl;
return 0;
}
}
GLboolean BufferObject::Extensions::glUnmapBuffer (GLenum target) const
{
if (_glUnmapBuffer) return _glUnmapBuffer(target);
else
{
notify(WARN)<<"Error: glUnmapBuffer not supported by OpenGL driver"<<std::endl;
return GL_FALSE;
}
}
void BufferObject::Extensions::glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params) const
{
if (_glGetBufferParameteriv) _glGetBufferParameteriv(target,pname,params);
else notify(WARN)<<"Error: glGetBufferParameteriv not supported by OpenGL driver"<<std::endl;
}
void BufferObject::Extensions::glGetBufferPointerv (GLenum target, GLenum pname, GLvoid* *params) const
{
if (_glGetBufferPointerv) _glGetBufferPointerv(target,pname,params);
else notify(WARN)<<"Error: glGetBufferPointerv not supported by OpenGL driver"<<std::endl;
}
//////////////////////////////////////////////////////////////////////////////////
//
// VertexBufferObject
//
VertexBufferObject::VertexBufferObject()
{
_target = GL_ARRAY_BUFFER_ARB;
_usage = GL_STATIC_DRAW_ARB;
// _usage = GL_DYNAMIC_DRAW_ARB;
// _usage = GL_STREAM_DRAW_ARB;
}
VertexBufferObject::VertexBufferObject(const VertexBufferObject& vbo,const CopyOp& copyop):
BufferObject(vbo,copyop)
{
}
VertexBufferObject::~VertexBufferObject()
{
}
unsigned int VertexBufferObject::addArray(osg::Array* array)
{
unsigned int i = _bufferEntryArrayPairs.size();
_bufferEntryArrayPairs.resize(i+1);
_bufferEntryArrayPairs[i].second = array;
_bufferEntryArrayPairs[i].first.modifiedCount.setAllElementsTo(0xffffffff);
_bufferEntryArrayPairs[i].first.offset = 0;
dirty();
return i;
}
void VertexBufferObject::removeArray(osg::Array* array)
{
BufferEntryArrayPairs::iterator itr;
for(itr = _bufferEntryArrayPairs.begin();
itr != _bufferEntryArrayPairs.end();
++itr)
{
if (itr->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="<<state.getFrameStamp()->getFrameNumber()<<std::endl;
unsigned int totalSizeRequired = 0;
unsigned int numModified = 0;
unsigned int numNotModified = 0;
for(BufferEntryArrayPairs::const_iterator itr = _bufferEntryArrayPairs.begin();
itr != _bufferEntryArrayPairs.end();
++itr)
{
const BufferEntryArrayPair& bep = *itr;
if (bep.second)
{
totalSizeRequired += bep.second->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 "<<bep.first.dataSize<<" bytes"<<std::endl;
if (vboMemory)
memcpy((char*)vboMemory + 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 (vboMemory) extensions->glUnmapBuffer(_target);
// osg::notify(osg::NOTICE)<<"pbo _totalSize="<<_totalSize<<std::endl;
// osg::notify(osg::NOTICE)<<"pbo "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
}
void VertexBufferObject::resizeGLObjectBuffers(unsigned int maxSize)
{
BufferObject::resizeGLObjectBuffers(maxSize);
for(BufferEntryArrayPairs::iterator itr = _bufferEntryArrayPairs.begin();
itr != _bufferEntryArrayPairs.end();
++itr)
{
itr->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"<<std::endl;
Extensions* extensions = getExtensions(contextID,true);
unsigned int totalSizeRequired = 0;
unsigned int numModified = 0;
unsigned int numNotModified = 0;
for(BufferEntryDrawElementsPairs::const_iterator itr = _bufferEntryDrawElementsPairs.begin();
itr != _bufferEntryDrawElementsPairs.end();
++itr)
{
const BufferEntryDrawElementstPair& bep = *itr;
if (bep.second)
{
totalSizeRequired += bep.second->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 BufferEntryDrawElementstPair& 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<<std::endl;
// osg::notify(osg::NOTICE)<<"pbo "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
}
void ElementBufferObject::resizeGLObjectBuffers(unsigned int maxSize)
{
BufferObject::resizeGLObjectBuffers(maxSize);
for(BufferEntryDrawElementsPairs::iterator itr = _bufferEntryDrawElementsPairs.begin();
itr != _bufferEntryDrawElementsPairs.end();
++itr)
{
itr->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<<std::endl;
// osg::notify(osg::NOTICE)<<"pbo "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
}
void PixelBufferObject::resizeGLObjectBuffers(unsigned int maxSize)
{
BufferObject::resizeGLObjectBuffers(maxSize);
_bufferEntryImagePair.first.modifiedCount.resize(maxSize);
}