/*********************************************************************** * Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. * * All rights reserved. * * Distributed under the terms of the MIT License (see LICENSE.TXT). * **********************************************************************/ #ifndef ACGL_OPENGL_OBJECTS_BUFFER_HH #define ACGL_OPENGL_OBJECTS_BUFFER_HH /** * A generic OpenGL Buffer Object * * Mostly an OpenGL Buffer Wrapper: names of OpenGL calls are stripped of the * 'gl' and 'Buffer' tokens and setters got a 'set' prefix. * * Calls that give the target the buffer should get bound to have an alternative * call that uses the last used or set target. * * Note: Most methods will bind this buffer! */ #include #include #include #include #include #include namespace ACGL{ namespace OpenGL{ /** * A minimal(!) wrapper to allow multiple Buffer objects pointing to the same * OpenGL resource (like an ArrayBuffer and a TransformFeedbackBuffer). * * This has to be an extra object so all Buffer types can inherit from Buffer * below to allow a unified API. */ class BufferObject { ACGL_NOT_COPYABLE(BufferObject) public: BufferObject() { mObjectName = 0; glGenBuffers(1, &mObjectName); } ~BufferObject(void) { // buffer 0 will get ignored by OpenGL glDeleteBuffers(1, &mObjectName); } GLuint mObjectName; //! has the side effect of binding this buffer. //! returned value is in bytes GLsizei getSize( GLenum _asTarget ) { glBindBuffer( _asTarget, mObjectName ); GLint value; glGetBufferParameteriv( _asTarget, GL_BUFFER_SIZE, &value ); return value; } }; typedef ptr::shared_ptr SharedBufferObject; /** * Buffers are general OpenGL Buffer Wrapper. * The OpenGL resource itself is attached via a shared pointer (GLBufferObject). * This was multiple Buffers can use internaly the same OpenGL resource, this is * useful if one resource should get interpreted as _different_ buffer types * so in that case the same GLBufferObject will get attached to different * Subclass Objects. * * Note: Subclasses should set the mTarget in there constructors! */ class Buffer { // ========================================================================================================= \/ // ============================================================================================ CONSTRUCTORS \/ // ========================================================================================================= \/ public: //! Most common default: a new Buffer corresponds to a new GL resource. You might decide on a binding point //! now or later. Buffer( GLenum _target ) : mSize(0), mTarget(_target) { mBuffer = SharedBufferObject( new BufferObject() ); } /** * Init with a given, external GL resource. * * Calling this with: * Buffer b( SharedGLBufferObject(NULL) ); * Is the way to _explicitly_ state that a real OpenGL resource will get added later. * In this case no GL wrapper calls should ever get called until one gets set! */ Buffer( SharedBufferObject _pBuffer, GLenum _target ) : mBuffer( _pBuffer ), mTarget(_target) { mSize = (!mBuffer)?0:mBuffer->getSize( mTarget ); } virtual ~Buffer(){} // ==================================================================================================== \/ // ============================================================================================ GETTERS \/ // ==================================================================================================== \/ public: inline GLuint getObjectName (void) const { return mBuffer->mObjectName; } inline bool isValid (void) const { return (mBuffer && glIsBuffer( mBuffer->mObjectName ) ); } inline SharedBufferObject getBufferObject () const { return mBuffer; } // ==================================================================================================== \/ // ============================================================================================ SETTERS \/ // ==================================================================================================== \/ //! the GL buffer can get changed at any time void setBufferObject( SharedBufferObject _pBuffer ) { mBuffer = _pBuffer; mSize = (!mBuffer)?0:mBuffer->getSize( mTarget ); } // ===================================================================================================== \/ // ============================================================================================ WRAPPERS \/ // ===================================================================================================== \/ private: inline GLint getParameter( GLenum _parameter ) const { bind( mTarget ); GLint value; glGetBufferParameteriv( mTarget, _parameter, &value ); return value; } #if ( !defined(ACGL_OPENGL_ES) || (ACGL_OPENGLES_VERSION >= 30)) // desktop or ES 3.0 onwards: #if (ACGL_OPENGL_VERSION >= 32) inline GLint64 getParameter64( GLenum _parameter ) const { bind( mTarget ); GLint64 value; glGetBufferParameteri64v( mTarget, _parameter, &value ); return value; } public: //! not side effect free! will bind this buffer to it's last target! //! caching of these values on RAM could be a good idea if needed very(!) often (as it's done with the size)! //inline GLint64 getSize() const { return getParameter64( GL_BUFFER_SIZE ); } inline GLint64 getMapOffset() const { return getParameter64( GL_BUFFER_MAP_OFFSET ); } inline GLint64 getMapLength() const { return getParameter64( GL_BUFFER_MAP_LENGTH ); } #else // OpenGL pre 3.2 or OpenGL ES 3.0: public: //inline GLint getSize() const { return getParameter ( GL_BUFFER_SIZE ); } inline GLint getMapOffset() const { return getParameter ( GL_BUFFER_MAP_OFFSET ); } inline GLint getMapLength() const { return getParameter ( GL_BUFFER_MAP_LENGTH ); } #endif // OpenGL >= 3.2 inline GLenum getAccess() const { return (GLenum) getParameter ( GL_BUFFER_ACCESS ); } inline GLint getAccessFlags() const { return (GLint) getParameter ( GL_BUFFER_ACCESS_FLAGS ); } inline GLboolean isMapped() const { return (GLboolean) getParameter ( GL_BUFFER_MAPPED ); } #endif // desktop & ES 3 public: inline GLenum getUsage() const { return (GLenum) getParameter ( GL_BUFFER_USAGE ); } //! the size is in bytes inline GLint64 getSize() const { return mSize; } //! Bind this buffer inline void bind( GLenum _target ) const { glBindBuffer( _target, mBuffer->mObjectName ); } //! Bind this buffer to its target inline void bind() const { glBindBuffer( mTarget, mBuffer->mObjectName ); } //! Set data for this buffer. Use only to init the buffer! //! Note: The function is not const, because it changes the corresponding GPU data inline void setData( GLenum _target, GLsizeiptr _sizeInBytes, const GLvoid *_pData = NULL, GLenum _usage = GL_STATIC_DRAW ) { mSize = _sizeInBytes; bind( _target ); glBufferData( _target, _sizeInBytes, _pData, _usage ); } //! Set data for this buffer at the last used target. Use only to init the buffer! inline void setData( GLsizeiptr _sizeInBytes, const GLvoid *_pData = NULL, GLenum _usage = GL_STATIC_DRAW ) { setData( mTarget, _sizeInBytes, _pData, _usage ); } //! Use this to modify the buffer inline void setSubData( GLenum _target, GLintptr _offset, GLsizeiptr _sizeInBytes, const GLvoid *_pData ) { bind( _target ); glBufferSubData( _target, _offset, _sizeInBytes, _pData ); } //! Use this to modify the buffer inline void setSubData( GLintptr _offset, GLsizeiptr _sizeInBytes, const GLvoid *_pData ) { setSubData( mTarget, _offset, _sizeInBytes, _pData ); } #if ((ACGL_OPENGL_VERSION >= 30) || (ACGL_OPENGLES_VERSION >= 30)) /** Map a part of the buffer to client memory * _offset & _length are values in bytes relative to the buffer * _access must contain one (or both) of: * GL_MAP_READ_BIT and GL_MAP_WRITE_BIT * and optionally: * GL_MAP_INVALIDATE_RANGE_BIT GL_MAP_INVALIDATE_BUFFER_BIT * GL_MAP_FLUSH_EXPLICIT_BIT GL_MAP_UNSYNCHRONIZED_BIT */ GLvoid *mapRange( GLenum _target, GLintptr _offset, GLsizeiptr _length, GLbitfield _access ) { bind( _target ); GLvoid *ret = glMapBufferRange( _target, _offset, _length, _access ); return ret; } inline GLvoid *mapRange( GLintptr _offset, GLsizeiptr _length, GLbitfield _access ) { return mapRange( mTarget, _offset, _length, _access ); } /** * Spec: * If a buffer is mapped with the GL_MAP_FLUSH_EXPLICIT_BIT flag, modifications * to the mapped range may be indicated by calling this. * _offset and _length indicate a modified subrange of the mapping, in byte. The specified * subrange to flush is relative to the start of the currently mapped range of buffer. * This can be called multiple times to indicate distinct subranges * of the mapping which require flushing. */ void flushMappedRange( GLenum _target, GLsizeiptr _offset, GLsizeiptr _length ) { bind( _target ); glFlushMappedBufferRange( _target, _offset, _length ); } inline void flushMappedRange( GLintptr _offset, GLsizeiptr _length ) { flushMappedRange( mTarget, _offset, _length ); } //! valid targets are only GL_TRANSFORM_FEEDBACK_BUFFER and GL_UNIFORM_BUFFER inline void bindBufferRange( GLenum _target, GLuint _index, GLintptr _offset, GLsizeiptr _size ) { glBindBufferRange( _target, _index, mBuffer->mObjectName, _offset, _size ); } //! maps a subset of the buffer defined by _offset and _size //! valid targets are only GL_TRANSFORM_FEEDBACK_BUFFER and GL_UNIFORM_BUFFER inline void bindBufferRange( GLuint _index, GLintptr _offset, GLsizeiptr _size ) { glBindBufferRange( mTarget, _index, mBuffer->mObjectName, _offset, _size ); } //! maps the full buffer to the given index (binding point) //! valid targets are only GL_TRANSFORM_FEEDBACK_BUFFER and GL_UNIFORM_BUFFER inline void bindBufferBase( GLenum _target, GLuint _index ) { glBindBufferBase( _target, _index, mBuffer->mObjectName ); } //! maps the full buffer to the given index (binding point) //! valid targets are only GL_TRANSFORM_FEEDBACK_BUFFER and GL_UNIFORM_BUFFER inline void bindBufferBase( GLuint _index ) { glBindBufferBase( mTarget, _index, mBuffer->mObjectName ); } #endif // OpenGL >= 3.0 #ifndef ACGL_OPENGLES_VERSION_20 //! Maps the whole buffer, if using GL 3+, better use mapRange! //! _access is GL_READ_ONLY GL_WRITE_ONLY or GL_READ_WRITE GLvoid *map( GLenum _target, GLenum _access ) { bind( _target ); GLvoid *ret = glMapBuffer( _target, _access ); return ret; } inline GLvoid *map( GLenum _access ) { return map( mTarget, _access ); } GLboolean unmap( GLenum _target ) { bind( _target ); GLboolean ret = glUnmapBuffer( _target ); return ret; } inline GLboolean unmap() { return unmap( mTarget ); } #endif // TODO: CopyBufferSubData /** * _target must be one of: GL_ARRAY_BUFFER GL_ATOMIC_COUNTER_BUFFER GL_COPY_READ_BUFFER GL_COPY_WRITE_BUFFER GL_DRAW_INDIRECT_BUFFER GL_ELEMENT_ARRAY_BUFFER GL_PIXEL_PACK_BUFFER GL_PIXEL_UNPACK_BUFFER GL_TEXTURE_BUFFER GL_TRANSFORM_FEEDBACK_BUFFER GL_UNIFORM_BUFFER * Can be changed at any time. * * Subclasses should overload this with a non-working function (+ a warning) * because an X-Buffer should not be attached _per default_ to Y! * Subclass buffers can however always use the method calls / binds with an * _explicit_ target (that doesn't match there one ones): * * XBuffer x; * x.bind( Y ); // ok, hope the programmer knowns what s/he does * * x.setTarget( Y ); // this is just calling for unintended side-effects! * x.bind(); */ virtual inline void setTarget( GLenum _target ) { mTarget = _target; } // =================================================================================================== \/ // ============================================================================================ FIELDS \/ // =================================================================================================== \/ protected: GLint64 mSize; // as this might get queried often (e.g. ArrayBuffer) we will explicitly mirror it in RAM - bytes SharedBufferObject mBuffer; GLenum mTarget; }; ACGL_SMARTPOINTER_TYPEDEFS(Buffer) } // OpenGL } // ACGL #endif // ACGL_OPENGL_OBJECTS_BUFFER_HH