/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * Copyright (C) 2012 David Callu * * 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. */ #ifndef OSG_BUFFEROBJECT #define OSG_BUFFEROBJECT 1 #include #include #include #include #include #include #include #include #include #ifndef GL_ARB_vertex_buffer_object #define GL_ARRAY_BUFFER_ARB 0x8892 #define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 #define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 #define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 #define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 #define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 #define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 #define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 #define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A #define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B #define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C #define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D #define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F #define GL_STREAM_DRAW_ARB 0x88E0 #define GL_STREAM_READ_ARB 0x88E1 #define GL_STREAM_COPY_ARB 0x88E2 #define GL_STATIC_DRAW_ARB 0x88E4 #define GL_STATIC_READ_ARB 0x88E5 #define GL_STATIC_COPY_ARB 0x88E6 #define GL_DYNAMIC_DRAW_ARB 0x88E8 #define GL_DYNAMIC_READ_ARB 0x88E9 #define GL_DYNAMIC_COPY_ARB 0x88EA #define GL_READ_ONLY_ARB 0x88B8 #define GL_WRITE_ONLY_ARB 0x88B9 #define GL_READ_WRITE_ARB 0x88BA #define GL_BUFFER_SIZE_ARB 0x8764 #define GL_BUFFER_USAGE_ARB 0x8765 #define GL_BUFFER_ACCESS_ARB 0x88BB #define GL_BUFFER_MAPPED_ARB 0x88BC #define GL_BUFFER_MAP_POINTER_ARB 0x88BD #endif #ifndef GL_VERSION_1_5 #define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_READ 0x88E1 #define GL_STREAM_COPY 0x88E2 #define GL_STATIC_DRAW 0x88E4 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_COPY 0x88E6 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #endif #ifndef GL_VERSION_2_1 #define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_PIXEL_UNPACK_BUFFER 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF #endif #ifndef GL_ARB_pixel_buffer_object #define GL_PIXEL_PACK_BUFFER_ARB 0x88EB #define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF #endif namespace osg { class State; class BufferData; class BufferObject; class BufferObjectProfile { public: BufferObjectProfile(): _target(0), _usage(0), _size(0) {} BufferObjectProfile(GLenum target, GLenum usage, unsigned int size): _target(target), _usage(usage), _size(size) {} BufferObjectProfile(const BufferObjectProfile& bpo): _target(bpo._target), _usage(bpo._usage), _size(bpo._size) {} bool operator < (const BufferObjectProfile& rhs) const { if (_target < rhs._target) return true; else if (_target > rhs._target) return false; if (_usage < rhs._usage) return true; else if (_usage > rhs._usage) return false; return _size < rhs._size; } bool operator == (const BufferObjectProfile& rhs) const { return (_target == rhs._target) && (_usage == rhs._usage) && (_size == rhs._size); } void setProfile(GLenum target, GLenum usage, unsigned int size) { _target = target; _usage = usage; _size = size; } BufferObjectProfile& operator = (const BufferObjectProfile& rhs) { _target = rhs._target; _usage = rhs._usage; _size = rhs._size; return *this; } GLenum _target; GLenum _usage; GLenum _size; }; // forward declare class GLBufferObjectSet; class GLBufferObjectManager; class OSG_EXPORT GLBufferObject : public GraphicsObject { public: GLBufferObject(unsigned int contextID, BufferObject* bufferObject, unsigned int glObjectID=0); void setProfile(const BufferObjectProfile& profile) { _profile = profile; } const BufferObjectProfile& getProfile() const { return _profile; } void setBufferObject(BufferObject* bufferObject); BufferObject* getBufferObject() { return _bufferObject; } struct BufferEntry { BufferEntry(): numRead(0), modifiedCount(0),dataSize(0),offset(0),dataSource(0) {} BufferEntry(const BufferEntry& rhs): numRead(rhs.numRead), modifiedCount(rhs.modifiedCount), dataSize(rhs.dataSize), offset(rhs.offset), dataSource(rhs.dataSource) {} BufferEntry& operator = (const BufferEntry& rhs) { if (&rhs==this) return *this; numRead = rhs.numRead; modifiedCount = rhs.modifiedCount; dataSize = rhs.dataSize; offset = rhs.offset; dataSource = rhs.dataSource; return *this; } unsigned int getNumClients() const; unsigned int numRead; unsigned int modifiedCount; unsigned int dataSize; unsigned int offset; BufferData* dataSource; }; inline unsigned int getContextID() const { return _contextID; } inline GLuint& getGLObjectID() { return _glObjectID; } inline GLuint getGLObjectID() const { return _glObjectID; } inline GLsizeiptr getOffset(unsigned int i) const { return _bufferEntries[i].offset; } inline void bindBuffer(); inline void unbindBuffer() { _extensions->glBindBuffer(_profile._target,0); } /** release GLBufferObject to the orphan list to be reused or deleted.*/ void release(); inline bool isDirty() const { return _dirty; } void dirty() { _dirty = true; } void clear(); void compileBuffer(); void deleteGLObject(); void assign(BufferObject* bufferObject); bool isPBOSupported() const { return _extensions->isPBOSupported; } bool hasAllBufferDataBeenRead() const; void setBufferDataHasBeenRead(const osg::BufferData* bd); protected: virtual ~GLBufferObject(); unsigned int computeBufferAlignment(unsigned int pos, unsigned int bufferAlignment) const { if (bufferAlignment<2) return pos; if ((pos%bufferAlignment)==0) return pos; return ((pos/bufferAlignment)+1)*bufferAlignment; } unsigned int _contextID; GLuint _glObjectID; BufferObjectProfile _profile; unsigned int _allocatedSize; bool _dirty; typedef std::vector BufferEntries; BufferEntries _bufferEntries; BufferObject* _bufferObject; public: GLBufferObjectSet* _set; GLBufferObject* _previous; GLBufferObject* _next; unsigned int _frameLastUsed; public: GLExtensions* _extensions; }; typedef std::list< ref_ptr > GLBufferObjectList; class OSG_EXPORT GLBufferObjectSet : public Referenced { public: GLBufferObjectSet(GLBufferObjectManager* parent, const BufferObjectProfile& profile); const BufferObjectProfile& getProfile() const { return _profile; } void handlePendingOrphandedGLBufferObjects(); void deleteAllGLBufferObjects(); void discardAllGLBufferObjects(); void flushAllDeletedGLBufferObjects(); void discardAllDeletedGLBufferObjects(); void flushDeletedGLBufferObjects(double currentTime, double& availableTime); osg::ref_ptr takeFromOrphans(BufferObject* bufferObject); osg::ref_ptr takeOrGenerate(BufferObject* bufferObject); void moveToBack(GLBufferObject* to); void addToBack(GLBufferObject* to); void orphan(GLBufferObject* to); void remove(GLBufferObject* to); void moveToSet(GLBufferObject* to, GLBufferObjectSet* set); unsigned int size() const { return _profile._size * _numOfGLBufferObjects; } bool makeSpace(unsigned int& size); bool checkConsistency() const; GLBufferObjectManager* getParent() { return _parent; } unsigned int computeNumGLBufferObjectsInList() const; unsigned int getNumOfGLBufferObjects() const { return _numOfGLBufferObjects; } unsigned int getNumOrphans() const { return static_cast(_orphanedGLBufferObjects.size()); } unsigned int getNumPendingOrphans() const { return static_cast(_pendingOrphanedGLBufferObjects.size()); } protected: virtual ~GLBufferObjectSet(); OpenThreads::Mutex _mutex; GLBufferObjectManager* _parent; unsigned int _contextID; BufferObjectProfile _profile; unsigned int _numOfGLBufferObjects; GLBufferObjectList _orphanedGLBufferObjects; GLBufferObjectList _pendingOrphanedGLBufferObjects; GLBufferObject* _head; GLBufferObject* _tail; }; class OSG_EXPORT GLBufferObjectManager : public GraphicsObjectManager { public: GLBufferObjectManager(unsigned int contextID); void setNumberActiveGLBufferObjects(unsigned int size) { _numActiveGLBufferObjects = size; } unsigned int& getNumberActiveGLBufferObjects() { return _numActiveGLBufferObjects; } unsigned int getNumberActiveGLBufferObjects() const { return _numActiveGLBufferObjects; } void setNumberOrphanedGLBufferObjects(unsigned int size) { _numOrphanedGLBufferObjects = size; } unsigned int& getNumberOrphanedGLBufferObjects() { return _numOrphanedGLBufferObjects; } unsigned int getNumberOrphanedGLBufferObjects() const { return _numOrphanedGLBufferObjects; } void setCurrGLBufferObjectPoolSize(unsigned int size) { _currGLBufferObjectPoolSize = size; } unsigned int& getCurrGLBufferObjectPoolSize() { return _currGLBufferObjectPoolSize; } unsigned int getCurrGLBufferObjectPoolSize() const { return _currGLBufferObjectPoolSize; } void setMaxGLBufferObjectPoolSize(unsigned int size); unsigned int getMaxGLBufferObjectPoolSize() const { return _maxGLBufferObjectPoolSize; } bool hasSpace(unsigned int size) const { return (_currGLBufferObjectPoolSize+size)<=_maxGLBufferObjectPoolSize; } bool makeSpace(unsigned int size); osg::ref_ptr generateGLBufferObject(const osg::BufferObject* bufferObject); void handlePendingOrphandedGLBufferObjects(); void deleteAllGLObjects(); void discardAllGLObjects(); void flushAllDeletedGLObjects(); void discardAllDeletedGLObjects(); void flushDeletedGLObjects(double currentTime, double& availableTime); GLBufferObjectSet* getGLBufferObjectSet(const BufferObjectProfile& profile); void newFrame(osg::FrameStamp* fs); void resetStats(); void reportStats(std::ostream& out); void recomputeStats(std::ostream& out) const; unsigned int& getFrameNumber() { return _frameNumber; } unsigned int& getNumberFrames() { return _numFrames; } unsigned int& getNumberDeleted() { return _numDeleted; } double& getDeleteTime() { return _deleteTime; } unsigned int& getNumberGenerated() { return _numGenerated; } double& getGenerateTime() { return _generateTime; } unsigned int& getNumberApplied() { return _numApplied; } double& getApplyTime() { return _applyTime; } protected: virtual ~GLBufferObjectManager(); typedef std::map< BufferObjectProfile, osg::ref_ptr > GLBufferObjectSetMap; unsigned int _numActiveGLBufferObjects; unsigned int _numOrphanedGLBufferObjects; unsigned int _currGLBufferObjectPoolSize; unsigned int _maxGLBufferObjectPoolSize; GLBufferObjectSetMap _glBufferObjectSetMap; unsigned int _frameNumber; unsigned int _numFrames; unsigned int _numDeleted; double _deleteTime; unsigned int _numGenerated; double _generateTime; unsigned int _numApplied; double _applyTime; }; class OSG_EXPORT BufferObject : public Object { public: BufferObject(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ BufferObject(const BufferObject& bo,const CopyOp& copyop=CopyOp::SHALLOW_COPY); virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast(obj)!=NULL; } virtual const char* libraryName() const { return "osg"; } virtual const char* className() const { return "BufferObject"; } void setTarget(GLenum target) { _profile._target = target; } GLenum getTarget() const { return _profile._target; } /** Set what type of usage the buffer object will have. Options are: * GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, * GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, * GL_DYNAMIC_DRAW, GL_DYNAMIC_READ, or GL_DYNAMIC_COPY. */ void setUsage(GLenum usage) { _profile._usage = usage; } /** Get the type of usage the buffer object has been set up for.*/ GLenum getUsage() const { return _profile._usage; } BufferObjectProfile& getProfile() { return _profile; } const BufferObjectProfile& getProfile() const { return _profile; } /** Set whether the BufferObject should use a GLBufferObject just for copying the BufferData and release it immmediately so that it may be reused.*/ void setCopyDataAndReleaseGLBufferObject(bool copyAndRelease) { _copyDataAndReleaseGLBufferObject = copyAndRelease; } /** Get whether the BufferObject should use a GLBufferObject just for copying the BufferData and release it immmediately.*/ bool getCopyDataAndReleaseGLBufferObject() const { return _copyDataAndReleaseGLBufferObject; } void dirty(); /** Resize any per context GLObject buffers to specified size. */ virtual void resizeGLObjectBuffers(unsigned int maxSize); /** If State is non-zero, this function releases OpenGL objects for * the specified graphics context. Otherwise, releases OpenGL objects * for all graphics contexts. */ void releaseGLObjects(State* state=0) const; unsigned int addBufferData(BufferData* bd); void removeBufferData(unsigned int index); void removeBufferData(BufferData* bd); void setBufferData(unsigned int index, BufferData* bd); BufferData* getBufferData(unsigned int index) { return _bufferDataList[index]; } const BufferData* getBufferData(unsigned int index) const { return _bufferDataList[index]; } unsigned int getNumBufferData() const { return static_cast(_bufferDataList.size()); } void setGLBufferObject(unsigned int contextID, GLBufferObject* glbo) { _glBufferObjects[contextID] = glbo; } GLBufferObject* getGLBufferObject(unsigned int contextID) const { return _glBufferObjects[contextID].get(); } GLBufferObject* getOrCreateGLBufferObject(unsigned int contextID) const; unsigned int computeRequiredBufferSize() const; /** deprecated, provided for backwards compatibility.*/ static void deleteBufferObject(unsigned int contextID,GLuint globj); protected: ~BufferObject(); typedef std::vector< BufferData* > BufferDataList; typedef osg::buffered_object< osg::ref_ptr > GLBufferObjects; BufferObjectProfile _profile; bool _copyDataAndReleaseGLBufferObject; BufferDataList _bufferDataList; mutable GLBufferObjects _glBufferObjects; }; class OSG_EXPORT BufferData : public Object { public: BufferData(): Object(true), _modifiedCount(0), _bufferIndex(0), _numClients(0) {} /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ BufferData(const BufferData& bd,const CopyOp& copyop=CopyOp::SHALLOW_COPY): osg::Object(bd,copyop), _modifiedCount(0), _bufferIndex(0), _modifiedCallback(bd._modifiedCallback), _numClients(0) {} virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast(obj)!=NULL; } virtual const char* libraryName() const { return "osg"; } virtual const char* className() const { return "BufferData"; } virtual const GLvoid* getDataPointer() const = 0; virtual unsigned int getTotalDataSize() const = 0; virtual osg::Array* asArray() { return 0; } virtual const osg::Array* asArray() const { return 0; } virtual osg::PrimitiveSet* asPrimitiveSet() { return 0; } virtual const osg::PrimitiveSet* asPrimitiveSet() const { return 0; } virtual osg::Image* asImage() { return 0; } virtual const osg::Image* asImage() const { return 0; } void setBufferObject(BufferObject* bufferObject); BufferObject* getBufferObject() { return _bufferObject.get(); } const BufferObject* getBufferObject() const { return _bufferObject.get(); } void setBufferIndex(unsigned int index) { _bufferIndex = index; } unsigned int getBufferIndex() const { return _bufferIndex; } GLBufferObject* getGLBufferObject(unsigned int contextID) const { return _bufferObject.valid() ? _bufferObject->getGLBufferObject(contextID) : 0; } GLBufferObject* getOrCreateGLBufferObject(unsigned int contextID) const { return _bufferObject.valid() ? _bufferObject->getOrCreateGLBufferObject(contextID) : 0; } struct ModifiedCallback : public virtual osg::Object { ModifiedCallback() {} ModifiedCallback(const ModifiedCallback& org, const CopyOp& copyop) : Object(org, copyop) {} META_Object(osg,ModifiedCallback); virtual void modified(BufferData* /*bufferData*/) const {} }; void setModifiedCallback(ModifiedCallback* md) { _modifiedCallback = md; } ModifiedCallback* getModifiedCallback() { return _modifiedCallback.get(); } const ModifiedCallback* getModifiedCallback() const { return _modifiedCallback.get(); } /** Dirty the primitive, which increments the modified count, to force buffer objects to update. * If a ModifiedCallback is attached to this BufferData then the callback is called prior to the bufferObject's dirty is called. */ inline void dirty() { ++_modifiedCount; if (_modifiedCallback.valid()) _modifiedCallback->modified(this); if (_bufferObject.valid()) _bufferObject->dirty(); } /** Set the modified count value.*/ inline void setModifiedCount(unsigned int value) { _modifiedCount=value; } /** Get modified count value.*/ inline unsigned int getModifiedCount() const { return _modifiedCount; } /** Resize any per context GLObject buffers to specified size. */ virtual void resizeGLObjectBuffers(unsigned int maxSize); /** If State is non-zero, this function releases OpenGL objects for * the specified graphics context. Otherwise, releases OpenGL objects * for all graphics contexts. */ void releaseGLObjects(State* state=0) const; unsigned int getNumClients() const { return _numClients; } void addClient(osg::Object * /*client*/) { ++_numClients; } void removeClient(osg::Object * /*client*/) { --_numClients; } protected: virtual ~BufferData(); unsigned int _modifiedCount; unsigned int _bufferIndex; osg::ref_ptr _bufferObject; osg::ref_ptr _modifiedCallback; unsigned int _numClients; }; class Array; class OSG_EXPORT VertexBufferObject : public BufferObject { public: VertexBufferObject(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ VertexBufferObject(const VertexBufferObject& vbo,const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg,VertexBufferObject); unsigned int addArray(osg::Array* array); void removeArray(osg::Array* array); void setArray(unsigned int i, Array* array); Array* getArray(unsigned int i); const Array* getArray(unsigned int i) const; protected: virtual ~VertexBufferObject(); }; class DrawElements; class OSG_EXPORT ElementBufferObject : public BufferObject { public: ElementBufferObject(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ ElementBufferObject(const ElementBufferObject& pbo,const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg,ElementBufferObject); unsigned int addDrawElements(osg::DrawElements* PrimitiveSet); void removeDrawElements(osg::DrawElements* PrimitiveSet); void setDrawElements(unsigned int i, DrawElements* PrimitiveSet); DrawElements* getDrawElements(unsigned int i); const DrawElements* getDrawElements(unsigned int i) const; protected: virtual ~ElementBufferObject(); }; class Image; class OSG_EXPORT PixelBufferObject : public BufferObject { public: PixelBufferObject(osg::Image* image=0); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ PixelBufferObject(const PixelBufferObject& pbo,const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg,PixelBufferObject); void setImage(osg::Image* image); Image* getImage(); const Image* getImage() const; bool isPBOSupported(unsigned int contextID) const { return _glBufferObjects[contextID]->isPBOSupported(); } protected: virtual ~PixelBufferObject(); }; /** * This object represent a general class of pixel buffer objects, * which are capable of allocating buffer object (memory) * on the GPU. The memory can then be used either for CPU-GPU * pixel transfer or directly for GPU-GPU transfer, without CPU intervention. **/ class OSG_EXPORT PixelDataBufferObject : public BufferObject { public: PixelDataBufferObject(); PixelDataBufferObject(const PixelDataBufferObject& pbo, const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg, PixelDataBufferObject); //! Set new size of the buffer object. This will reallocate the memory on the next compile inline void setDataSize(unsigned int size) { _profile._size = size; dirty(); } //! Get data size of the used buffer inline unsigned int getDataSize() const { return _profile._size; } //! Compile the buffer (reallocate the memory if buffer is dirty) virtual void compileBuffer(State& state) const; //! Bind the buffer in read mode, which means that data can be downloaded from the buffer (note: GL_PIXEL_UNPACK_BUFFER_ARB) virtual void bindBufferInReadMode(State& state); //! Bind the buffer in write mode, which means following OpenGL instructions will write data into the buffer (note: GL_PIXEL_PACK_BUFFER_ARB) virtual void bindBufferInWriteMode(State& state); //! Unbind the buffer virtual void unbindBuffer(unsigned int contextID) const; /** Resize any per context GLObject buffers to specified size. */ virtual void resizeGLObjectBuffers(unsigned int maxSize); enum Mode { //! A normal mode of this data buffer NONE = 0, //! Buffer is in read mode (@see bindBufferInReadMode) READ = 1, //! Buffer is in write mode (@see bindBufferInWriteMode) WRITE = 2 }; Mode getMode(unsigned int contextID) const { return (Mode)_mode[contextID]; } protected: virtual ~PixelDataBufferObject(); typedef osg::buffered_value ModeList; mutable ModeList _mode; }; class OSG_EXPORT UniformBufferObject : public BufferObject { public: UniformBufferObject(); UniformBufferObject(const UniformBufferObject& ubo, const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg, UniformBufferObject); protected: virtual ~UniformBufferObject(); }; class OSG_EXPORT AtomicCounterBufferObject : public BufferObject { public: AtomicCounterBufferObject(); AtomicCounterBufferObject(const AtomicCounterBufferObject& ubo, const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg, AtomicCounterBufferObject); protected: virtual ~AtomicCounterBufferObject(); }; inline void GLBufferObject::bindBuffer() { _extensions->glBindBuffer(_profile._target,_glObjectID); if (_set) _set->moveToBack(this); } class OSG_EXPORT ShaderStorageBufferObject : public BufferObject { public: ShaderStorageBufferObject(); ShaderStorageBufferObject(const ShaderStorageBufferObject& ubo, const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_Object(osg, ShaderStorageBufferObject); protected: virtual ~ShaderStorageBufferObject(); }; } #endif