diff --git a/src/Magnum/AbstractFramebuffer.cpp b/src/Magnum/AbstractFramebuffer.cpp index ca59de6f5..da487f866 100644 --- a/src/Magnum/AbstractFramebuffer.cpp +++ b/src/Magnum/AbstractFramebuffer.cpp @@ -89,14 +89,14 @@ Int AbstractFramebuffer::maxDualSourceDrawBuffers() { #endif void AbstractFramebuffer::createIfNotAlready() { - if(_created) return; + if(_flags & ObjectFlag::Created) return; /* glGen*() does not create the object, just reserves the name. Some commands (such as glObjectLabel()) operate with IDs directly and they require the object to be created. Binding the framebuffer finally creates it. Also all EXT DSA functions implicitly create it. */ bindInternal(); - CORRADE_INTERNAL_ASSERT(_created); + CORRADE_INTERNAL_ASSERT(_flags & ObjectFlag::Created); } void AbstractFramebuffer::bind() { @@ -141,7 +141,7 @@ void AbstractFramebuffer::bindImplementationDefault(FramebufferTarget target) { } else CORRADE_ASSERT_UNREACHABLE(); /* Binding the framebuffer finally creates it */ - _created = true; + _flags |= ObjectFlag::Created; glBindFramebuffer(GLenum(target), _id); } @@ -187,7 +187,7 @@ FramebufferTarget AbstractFramebuffer::bindImplementationDefault() { state.readBinding = _id; /* Binding the framebuffer finally creates it */ - _created = true; + _flags |= ObjectFlag::Created; glBindFramebuffer(GLenum(FramebufferTarget::Read), _id); return FramebufferTarget::Read; } @@ -361,7 +361,7 @@ GLenum AbstractFramebuffer::checkStatusImplementationDSA(const FramebufferTarget } GLenum AbstractFramebuffer::checkStatusImplementationDSAEXT(const FramebufferTarget target) { - _created = true; + _flags |= ObjectFlag::Created; return glCheckNamedFramebufferStatusEXT(_id, GLenum(target)); } #endif @@ -379,7 +379,7 @@ void AbstractFramebuffer::drawBuffersImplementationDSA(const GLsizei count, cons } void AbstractFramebuffer::drawBuffersImplementationDSAEXT(GLsizei count, const GLenum* buffers) { - _created = true; + _flags |= ObjectFlag::Created; glFramebufferDrawBuffersEXT(_id, count, buffers); } #endif @@ -423,7 +423,7 @@ void AbstractFramebuffer::drawBufferImplementationDSA(const GLenum buffer) { } void AbstractFramebuffer::drawBufferImplementationDSAEXT(GLenum buffer) { - _created = true; + _flags |= ObjectFlag::Created; glFramebufferDrawBufferEXT(_id, buffer); } #endif @@ -449,7 +449,7 @@ void AbstractFramebuffer::readBufferImplementationDSA(const GLenum buffer) { } void AbstractFramebuffer::readBufferImplementationDSAEXT(GLenum buffer) { - _created = true; + _flags |= ObjectFlag::Created; glFramebufferReadBufferEXT(_id, buffer); } #endif diff --git a/src/Magnum/AbstractFramebuffer.h b/src/Magnum/AbstractFramebuffer.h index 574c3d41b..80868eed4 100644 --- a/src/Magnum/AbstractFramebuffer.h +++ b/src/Magnum/AbstractFramebuffer.h @@ -32,8 +32,7 @@ #include #include -#include "Magnum/Magnum.h" -#include "Magnum/OpenGL.h" +#include "Magnum/AbstractObject.h" #include "Magnum/Math/Range.h" namespace Magnum { @@ -403,7 +402,9 @@ class MAGNUM_EXPORT AbstractFramebuffer { #else protected: #endif - explicit AbstractFramebuffer() = default; + explicit AbstractFramebuffer(): _flags{ObjectFlag::DeleteOnDestruction} {} + explicit AbstractFramebuffer(GLuint id, const Range2Di& viewport, ObjectFlags flags) noexcept: _id{id}, _viewport{viewport}, _flags{flags} {} + ~AbstractFramebuffer() = default; void MAGNUM_LOCAL createIfNotAlready(); @@ -413,8 +414,8 @@ class MAGNUM_EXPORT AbstractFramebuffer { void MAGNUM_LOCAL setViewportInternal(); GLuint _id; - bool _created; /* see createIfNotAlready() for details */ Range2Di _viewport; + ObjectFlags _flags; private: #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/AbstractObject.h b/src/Magnum/AbstractObject.h index e96f8a258..110472411 100644 --- a/src/Magnum/AbstractObject.h +++ b/src/Magnum/AbstractObject.h @@ -30,7 +30,7 @@ */ #include -#include +#include #include "Magnum/Magnum.h" #include "Magnum/OpenGL.h" @@ -40,6 +40,42 @@ namespace Magnum { namespace Implementation { struct DebugState; } +/** +@brief Object wrapping flag + +@see @ref ObjectFlags, @ref Buffer::wrap(), @ref BufferTexture::wrap(), + @ref CubeMapTexture::wrap(), @ref CubeMapTextureArray::wrap(), + @ref Framebuffer::wrap(), @ref Mesh::wrap(), + @ref MultisampleTexture::wrap(), @ref PrimitiveQuery::wrap(), + @ref RectangleTexture::wrap(), @ref Renderbuffer::wrap(), + @ref SampleQuery::wrap(), @ref Texture::wrap(), @ref TextureArray::wrap(), + @ref TimeQuery::wrap(), @ref TransformFeedback::wrap() +*/ +enum class ObjectFlag: UnsignedByte { + /** + * The object is known to be already created, either by using `glCreate*()` + * function or by binding object previously generated by `glGen*()` + * function. If you are not sure, don't specify this flag. + */ + Created = 1 << 0, + + /** Delete the object on destruction. */ + DeleteOnDestruction = 1 << 1 +}; + +/** +@brief Object wrapping flags + +@see @ref Buffer::wrap(), @ref BufferTexture::wrap(), + @ref CubeMapTexture::wrap(), @ref CubeMapTextureArray::wrap(), + @ref Framebuffer::wrap(), @ref Mesh::wrap(), + @ref MultisampleTexture::wrap(), @ref PrimitiveQuery::wrap(), + @ref RectangleTexture::wrap(), @ref Renderbuffer::wrap(), + @ref SampleQuery::wrap(), @ref Texture::wrap(), @ref TextureArray::wrap(), + @ref TimeQuery::wrap(), @ref TransformFeedback::wrap() +*/ +typedef Containers::EnumSet ObjectFlags; + /** @brief Base for all OpenGL objects */ @@ -80,6 +116,8 @@ class MAGNUM_EXPORT AbstractObject { #endif }; +CORRADE_ENUMSET_OPERATORS(ObjectFlags) + } #endif diff --git a/src/Magnum/AbstractQuery.cpp b/src/Magnum/AbstractQuery.cpp index 0ce9fb1d6..87db20b7c 100644 --- a/src/Magnum/AbstractQuery.cpp +++ b/src/Magnum/AbstractQuery.cpp @@ -36,19 +36,19 @@ namespace Magnum { -AbstractQuery::AbstractQuery(GLenum target): _target{target} { +AbstractQuery::AbstractQuery(GLenum target): _target{target}, _flags{ObjectFlag::DeleteOnDestruction} { (this->*Context::current()->state().query->createImplementation)(); } #ifdef MAGNUM_BUILD_DEPRECATED -AbstractQuery::AbstractQuery(): _target{} { +AbstractQuery::AbstractQuery(): _target{}, _flags{ObjectFlag::DeleteOnDestruction} { createImplementationDefault(); } #endif AbstractQuery::~AbstractQuery() { - /* Moved out, nothing to do */ - if(!_id) return; + /* Moved out or not deleting on destruction, nothing to do */ + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; #ifndef MAGNUM_TARGET_GLES2 glDeleteQueries(1, &_id); @@ -57,6 +57,7 @@ AbstractQuery::~AbstractQuery() { #else CORRADE_ASSERT_UNREACHABLE(); #endif + _flags |= ObjectFlag::Created; } void AbstractQuery::createImplementationDefault() { diff --git a/src/Magnum/AbstractQuery.h b/src/Magnum/AbstractQuery.h index 5f0c0730a..43b0c3931 100644 --- a/src/Magnum/AbstractQuery.h +++ b/src/Magnum/AbstractQuery.h @@ -69,6 +69,17 @@ class MAGNUM_EXPORT AbstractQuery: public AbstractObject { /** @brief OpenGL query ID */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL query object and returns its ID so it + * is not deleted on destruction. The internal state is then equivalent + * to moved-from state. + * @see @ref PrimitiveQuery::wrap(), @ref SampleQuery::wrap(), + * @ref TimeQuery::wrap() + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Query label @@ -153,7 +164,8 @@ class MAGNUM_EXPORT AbstractQuery: public AbstractObject { * @brief Destructor * * Deletes assigned OpenGL query. - * @see @fn_gl{DeleteQueries} + * @see @ref PrimitiveQuery::wrap(), @ref SampleQuery::wrap(), + * @ref TimeQuery::wrap(), @ref release(), @fn_gl{DeleteQueries} */ ~AbstractQuery(); @@ -161,6 +173,7 @@ class MAGNUM_EXPORT AbstractQuery: public AbstractObject { private: #endif explicit AbstractQuery(GLenum target); + explicit AbstractQuery(GLuint id, GLenum target, ObjectFlags flags) noexcept: _id{id}, _target{target}, _flags{flags} {} #ifdef MAGNUM_BUILD_DEPRECATED explicit AbstractQuery(); @@ -179,6 +192,7 @@ class MAGNUM_EXPORT AbstractQuery: public AbstractObject { GLuint _id; GLenum _target; + ObjectFlags _flags; }; #ifndef DOXYGEN_GENERATING_OUTPUT @@ -202,6 +216,12 @@ inline AbstractQuery& AbstractQuery::operator=(AbstractQuery&& other) noexcept { return *this; } +inline GLuint AbstractQuery::release() { + const GLuint id = _id; + _id = 0; + return id; +} + } #else #error this header is not available in WebGL 1.0 build diff --git a/src/Magnum/AbstractTexture.cpp b/src/Magnum/AbstractTexture.cpp index a61be3f14..341913cf4 100644 --- a/src/Magnum/AbstractTexture.cpp +++ b/src/Magnum/AbstractTexture.cpp @@ -204,26 +204,25 @@ void AbstractTexture::bindImplementationMulti(const GLint firstTextureUnit, Cont } #endif -AbstractTexture::AbstractTexture(GLenum target): _target{target} { +AbstractTexture::AbstractTexture(GLenum target): _target{target}, _flags{ObjectFlag::DeleteOnDestruction} { (this->*Context::current()->state().texture->createImplementation)(); CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding); } void AbstractTexture::createImplementationDefault() { glGenTextures(1, &_id); - _created = false; } #ifndef MAGNUM_TARGET_GLES void AbstractTexture::createImplementationDSA() { glCreateTextures(_target, 1, &_id); - _created = true; + _flags |= ObjectFlag::Created; } #endif AbstractTexture::~AbstractTexture() { - /* Moved out, nothing to do */ - if(!_id) return; + /* Moved out or not deleting on destruction, nothing to do */ + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; /* Remove all bindings */ for(auto& binding: Context::current()->state().texture->bindings) @@ -233,7 +232,7 @@ AbstractTexture::~AbstractTexture() { } inline void AbstractTexture::createIfNotAlready() { - if(_created) return; + if(_flags & ObjectFlag::Created) return; /* glGen*() does not create the object, just reserves the name. Some commands (such as glBindTextures() or glObjectLabel()) operate with IDs @@ -241,7 +240,7 @@ inline void AbstractTexture::createIfNotAlready() { to desired target finally creates it. Also all EXT DSA functions implicitly create it. */ bindInternal(); - CORRADE_INTERNAL_ASSERT(_created); + CORRADE_INTERNAL_ASSERT(_flags & ObjectFlag::Created); } #ifndef MAGNUM_TARGET_WEBGL @@ -276,7 +275,7 @@ void AbstractTexture::bindImplementationDefault(GLint textureUnit) { glActiveTexture(GL_TEXTURE0 + (textureState.currentTextureUnit = textureUnit)); /* Binding the texture finally creates it */ - _created = true; + _flags |= ObjectFlag::Created; glBindTexture(_target, _id); } @@ -291,7 +290,7 @@ void AbstractTexture::bindImplementationDSA(const GLint textureUnit) { } void AbstractTexture::bindImplementationDSAEXT(GLint textureUnit) { - _created = true; + _flags |= ObjectFlag::Created; glBindMultiTextureEXT(GL_TEXTURE0 + textureUnit, _target, _id); } #endif @@ -430,7 +429,7 @@ void AbstractTexture::mipmapImplementationDSA() { } void AbstractTexture::mipmapImplementationDSAEXT() { - _created = true; + _flags |= ObjectFlag::Created; glGenerateTextureMipmapEXT(_id, _target); } #endif @@ -457,7 +456,7 @@ void AbstractTexture::bindInternal() { textureState.bindings[internalTextureUnit] = {_target, _id}; /* Binding the texture finally creates it */ - _created = true; + _flags |= ObjectFlag::Created; glBindTexture(_target, _id); } @@ -873,7 +872,7 @@ void AbstractTexture::parameterImplementationDSA(const GLenum parameter, const G } void AbstractTexture::parameterImplementationDSAEXT(GLenum parameter, GLint value) { - _created = true; + _flags |= ObjectFlag::Created; glTextureParameteriEXT(_id, _target, parameter, value); } #endif @@ -889,7 +888,7 @@ void AbstractTexture::parameterImplementationDSA(const GLenum parameter, const G } void AbstractTexture::parameterImplementationDSAEXT(GLenum parameter, GLfloat value) { - _created = true; + _flags |= ObjectFlag::Created; glTextureParameterfEXT(_id, _target, parameter, value); } #endif @@ -906,7 +905,7 @@ void AbstractTexture::parameterImplementationDSA(const GLenum parameter, const G } void AbstractTexture::parameterImplementationDSAEXT(GLenum parameter, const GLint* values) { - _created = true; + _flags |= ObjectFlag::Created; glTextureParameterivEXT(_id, _target, parameter, values); } #endif @@ -923,7 +922,7 @@ void AbstractTexture::parameterImplementationDSA(const GLenum parameter, const G } void AbstractTexture::parameterImplementationDSAEXT(GLenum parameter, const GLfloat* values) { - _created = true; + _flags |= ObjectFlag::Created; glTextureParameterfvEXT(_id, _target, parameter, values); } #endif @@ -939,7 +938,7 @@ void AbstractTexture::parameterIImplementationDSA(const GLenum parameter, const } void AbstractTexture::parameterIImplementationDSAEXT(GLenum parameter, const GLuint* values) { - _created = true; + _flags |= ObjectFlag::Created; glTextureParameterIuivEXT(_id, _target, parameter, values); } @@ -953,7 +952,7 @@ void AbstractTexture::parameterIImplementationDSA(const GLenum parameter, const } void AbstractTexture::parameterIImplementationDSAEXT(GLenum parameter, const GLint* values) { - _created = true; + _flags |= ObjectFlag::Created; glTextureParameterIivEXT(_id, _target, parameter, values); } #endif @@ -976,7 +975,7 @@ void AbstractTexture::getLevelParameterImplementationDSA(const GLint level, cons } void AbstractTexture::getLevelParameterImplementationDSAEXT(GLint level, GLenum parameter, GLint* values) { - _created = true; + _flags |= ObjectFlag::Created; glGetTextureLevelParameterivEXT(_id, _target, level, parameter, values); } #endif @@ -1002,7 +1001,7 @@ void AbstractTexture::storageImplementationDSA(const GLsizei levels, const Textu } void AbstractTexture::storageImplementationDSAEXT(GLsizei levels, TextureFormat internalFormat, const Math::Vector<1, GLsizei>& size) { - _created = true; + _flags |= ObjectFlag::Created; glTextureStorage1DEXT(_id, _target, levels, GLenum(internalFormat), size[0]); } #endif @@ -1071,7 +1070,7 @@ void AbstractTexture::storageImplementationDSA(const GLsizei levels, const Textu } void AbstractTexture::storageImplementationDSAEXT(GLsizei levels, TextureFormat internalFormat, const Vector2i& size) { - _created = true; + _flags |= ObjectFlag::Created; glTextureStorage2DEXT(_id, _target, levels, GLenum(internalFormat), size.x(), size.y()); } #endif @@ -1130,7 +1129,7 @@ void AbstractTexture::storageImplementationDSA(const GLsizei levels, const Textu } void AbstractTexture::storageImplementationDSAEXT(GLsizei levels, TextureFormat internalFormat, const Vector3i& size) { - _created = true; + _flags |= ObjectFlag::Created; glTextureStorage3DEXT(_id, _target, levels, GLenum(internalFormat), size.x(), size.y(), size.z()); } #endif @@ -1155,7 +1154,7 @@ void AbstractTexture::storageMultisampleImplementationDSA(const GLsizei samples, } void AbstractTexture::storageMultisampleImplementationDSAEXT(const GLsizei samples, const TextureFormat internalFormat, const Vector2i& size, const GLboolean fixedSampleLocations) { - _created = true; + _flags |= ObjectFlag::Created; glTextureStorage2DMultisampleEXT(_id, _target, samples, GLenum(internalFormat), size.x(), size.y(), fixedSampleLocations); } @@ -1174,7 +1173,7 @@ void AbstractTexture::storageMultisampleImplementationDSA(const GLsizei samples, } void AbstractTexture::storageMultisampleImplementationDSAEXT(const GLsizei samples, const TextureFormat internalFormat, const Vector3i& size, const GLboolean fixedSampleLocations) { - _created = true; + _flags |= ObjectFlag::Created; glTextureStorage3DMultisampleEXT(_id, _target, samples, GLenum(internalFormat), size.x(), size.y(), size.z(), fixedSampleLocations); } #endif @@ -1190,7 +1189,7 @@ void AbstractTexture::getImageImplementationDSA(const GLint level, const ColorFo } void AbstractTexture::getImageImplementationDSAEXT(const GLint level, const ColorFormat format, const ColorType type, const std::size_t, GLvoid* const data) { - _created = true; + _flags |= ObjectFlag::Created; glGetTextureImageEXT(_id, _target, level, GLenum(format), GLenum(type), data); } @@ -1211,7 +1210,7 @@ void AbstractTexture::subImageImplementationDSA(const GLint level, const Math::V } void AbstractTexture::subImageImplementationDSAEXT(GLint level, const Math::Vector<1, GLint>& offset, const Math::Vector<1, GLsizei>& size, ColorFormat format, ColorType type, const GLvoid* data) { - _created = true; + _flags |= ObjectFlag::Created; glTextureSubImage1DEXT(_id, _target, level, offset[0], size[0], GLenum(format), GLenum(type), data); } #endif @@ -1227,7 +1226,7 @@ void AbstractTexture::subImageImplementationDSA(const GLint level, const Vector2 } void AbstractTexture::subImageImplementationDSAEXT(GLint level, const Vector2i& offset, const Vector2i& size, ColorFormat format, ColorType type, const GLvoid* data) { - _created = true; + _flags |= ObjectFlag::Created; glTextureSubImage2DEXT(_id, _target, level, offset.x(), offset.y(), size.x(), size.y(), GLenum(format), GLenum(type), data); } #endif @@ -1257,7 +1256,7 @@ void AbstractTexture::subImageImplementationDSA(const GLint level, const Vector3 } void AbstractTexture::subImageImplementationDSAEXT(GLint level, const Vector3i& offset, const Vector3i& size, ColorFormat format, ColorType type, const GLvoid* data) { - _created = true; + _flags |= ObjectFlag::Created; glTextureSubImage3DEXT(_id, _target, level, offset.x(), offset.y(), offset.z(), size.x(), size.y(), size.z(), GLenum(format), GLenum(type), data); } #endif diff --git a/src/Magnum/AbstractTexture.h b/src/Magnum/AbstractTexture.h index e8ab6f836..e00d9a966 100644 --- a/src/Magnum/AbstractTexture.h +++ b/src/Magnum/AbstractTexture.h @@ -259,7 +259,11 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { * @brief Destructor * * Deletes associated OpenGL texture. - * @see @fn_gl{DeleteTextures} + * @see @ref BufferTexture::wrap(), @ref CubeMapTexture::wrap(), + * @ref CubeMapTextureArray::wrap(), + * @ref MultisampleTexture::wrap(), @ref RectangleTexture::wrap(), + * @ref Texture::wrap(), @ref TextureArray::wrap(), + * @ref release(), @fn_gl{DeleteTextures} */ ~AbstractTexture(); @@ -272,6 +276,19 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { /** @brief OpenGL texture ID */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL texture object and returns its ID so it + * is not deleted on destruction. The internal state is then equivalent + * to moved-from state. + * @see @ref BufferTexture::wrap(), @ref CubeMapTexture::wrap(), + * @ref CubeMapTextureArray::wrap(), + * @ref MultisampleTexture::wrap(), @ref RectangleTexture::wrap(), + * @ref Texture::wrap(), @ref TextureArray::wrap() + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Texture label @@ -335,6 +352,7 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { template struct DataHelper {}; explicit AbstractTexture(GLenum target); + explicit AbstractTexture(GLuint id, GLenum target, ObjectFlags flags) noexcept: _target{target}, _id{id}, _flags{flags} {} #ifndef MAGNUM_TARGET_WEBGL AbstractTexture& setLabelInternal(Containers::ArrayReference label); @@ -556,7 +574,7 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { ColorType MAGNUM_LOCAL imageTypeForInternalFormat(TextureFormat internalFormat); GLuint _id; - bool _created; /* see createIfNotAlready() for details */ + ObjectFlags _flags; }; #ifndef DOXYGEN_GENERATING_OUTPUT @@ -670,7 +688,7 @@ template<> struct MAGNUM_EXPORT AbstractTexture::DataHelper<3> { }; #endif -inline AbstractTexture::AbstractTexture(AbstractTexture&& other) noexcept: _target{other._target}, _id{other._id}, _created{other._created} { +inline AbstractTexture::AbstractTexture(AbstractTexture&& other) noexcept: _target{other._target}, _id{other._id}, _flags{other._flags} { other._id = 0; } @@ -678,10 +696,16 @@ inline AbstractTexture& AbstractTexture::operator=(AbstractTexture&& other) noex using std::swap; swap(_target, other._target); swap(_id, other._id); - swap(_created, other._created); + swap(_flags, other._flags); return *this; } +inline GLuint AbstractTexture::release() { + const GLuint id = _id; + _id = 0; + return id; +} + } #endif diff --git a/src/Magnum/Buffer.cpp b/src/Magnum/Buffer.cpp index 26823ef7f..bbe9410a8 100644 --- a/src/Magnum/Buffer.cpp +++ b/src/Magnum/Buffer.cpp @@ -184,7 +184,7 @@ void Buffer::copy(Buffer& read, Buffer& write, const GLintptr readOffset, const } #endif -Buffer::Buffer(const TargetHint targetHint): _targetHint{targetHint} +Buffer::Buffer(const TargetHint targetHint): _targetHint{targetHint}, _flags{ObjectFlag::DeleteOnDestruction} #ifdef CORRADE_TARGET_NACL , _mappedBuffer{nullptr} #endif @@ -195,19 +195,18 @@ Buffer::Buffer(const TargetHint targetHint): _targetHint{targetHint} void Buffer::createImplementationDefault() { glGenBuffers(1, &_id); - _created = false; } #ifndef MAGNUM_TARGET_GLES void Buffer::createImplementationDSA() { glCreateBuffers(1, &_id); - _created = true; + _flags |= ObjectFlag::Created; } #endif Buffer::~Buffer() { - /* Moved out, nothing to do */ - if(!_id) return; + /* Moved out or not deleting on destruction, nothing to do */ + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; GLuint* bindings = Context::current()->state().buffer->bindings; @@ -219,7 +218,7 @@ Buffer::~Buffer() { } inline void Buffer::createIfNotAlready() { - if(_created) return; + if(_flags & ObjectFlag::Created) return; /* glGen*() does not create the object, just reserves the name. Some commands (such as glInvalidateBufferData() or glObjectLabel()) operate @@ -227,7 +226,7 @@ inline void Buffer::createIfNotAlready() { buffer finally creates it. Also all EXT DSA functions implicitly create it. */ bindSomewhereInternal(_targetHint); - CORRADE_INTERNAL_ASSERT(_created); + CORRADE_INTERNAL_ASSERT(_flags & ObjectFlag::Created); } #ifndef MAGNUM_TARGET_WEBGL @@ -260,7 +259,7 @@ void Buffer::bindInternal(const TargetHint target, Buffer* const buffer) { /* Bind the buffer otherwise, which will also finally create it */ bound = id; - if(buffer) buffer->_created = true; + if(buffer) buffer->_flags |= ObjectFlag::Created; glBindBuffer(GLenum(target), id); } @@ -278,7 +277,7 @@ auto Buffer::bindSomewhereInternal(const TargetHint hint) -> TargetHint { /* Bind the buffer to hint target otherwise */ hintBinding = _id; - _created = true; + _flags |= ObjectFlag::Created; glBindBuffer(GLenum(hint), _id); return hint; } @@ -443,7 +442,8 @@ void Buffer::copyImplementationDSA(Buffer& read, Buffer& write, const GLintptr r } void Buffer::copyImplementationDSAEXT(Buffer& read, Buffer& write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { - read._created = write._created = true; + read._flags |= ObjectFlag::Created; + write._flags |= ObjectFlag::Created; glNamedCopyBufferSubDataEXT(read._id, write._id, readOffset, writeOffset, size); } #endif @@ -459,7 +459,7 @@ void Buffer::getParameterImplementationDSA(const GLenum value, GLint* const data } void Buffer::getParameterImplementationDSAEXT(const GLenum value, GLint* const data) { - _created = true; + _flags |= ObjectFlag::Created; glGetNamedBufferParameterivEXT(_id, value, data); } #endif @@ -474,7 +474,7 @@ void Buffer::getSubDataImplementationDSA(const GLintptr offset, const GLsizeiptr } void Buffer::getSubDataImplementationDSAEXT(const GLintptr offset, const GLsizeiptr size, GLvoid* const data) { - _created = true; + _flags |= ObjectFlag::Created; glGetNamedBufferSubDataEXT(_id, offset, size, data); } #endif @@ -489,7 +489,7 @@ void Buffer::dataImplementationDSA(const GLsizeiptr size, const GLvoid* const da } void Buffer::dataImplementationDSAEXT(GLsizeiptr size, const GLvoid* data, BufferUsage usage) { - _created = true; + _flags |= ObjectFlag::Created; glNamedBufferDataEXT(_id, size, data, GLenum(usage)); } #endif @@ -504,7 +504,7 @@ void Buffer::subDataImplementationDSA(const GLintptr offset, const GLsizeiptr si } void Buffer::subDataImplementationDSAEXT(GLintptr offset, GLsizeiptr size, const GLvoid* data) { - _created = true; + _flags |= ObjectFlag::Created; glNamedBufferSubDataEXT(_id, offset, size, data); } #endif @@ -545,7 +545,7 @@ void* Buffer::mapImplementationDSA(const MapAccess access) { } void* Buffer::mapImplementationDSAEXT(MapAccess access) { - _created = true; + _flags |= ObjectFlag::Created; return glMapNamedBufferEXT(_id, GLenum(access)); } #endif @@ -569,7 +569,7 @@ void* Buffer::mapRangeImplementationDSA(const GLintptr offset, const GLsizeiptr } void* Buffer::mapRangeImplementationDSAEXT(GLintptr offset, GLsizeiptr length, MapFlags access) { - _created = true; + _flags |= ObjectFlag::Created; return glMapNamedBufferRangeEXT(_id, offset, length, GLenum(access)); } #endif @@ -592,7 +592,7 @@ void Buffer::flushMappedRangeImplementationDSA(const GLintptr offset, const GLsi } void Buffer::flushMappedRangeImplementationDSAEXT(GLintptr offset, GLsizeiptr length) { - _created = true; + _flags |= ObjectFlag::Created; glFlushMappedNamedBufferRangeEXT(_id, offset, length); } #endif @@ -613,7 +613,7 @@ bool Buffer::unmapImplementationDSA() { } bool Buffer::unmapImplementationDSAEXT() { - _created = true; + _flags |= ObjectFlag::Created; return glUnmapNamedBufferEXT(_id); } #endif diff --git a/src/Magnum/Buffer.h b/src/Magnum/Buffer.h index 56a8e2a63..a74278670 100644 --- a/src/Magnum/Buffer.h +++ b/src/Magnum/Buffer.h @@ -820,6 +820,28 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { static void copy(Buffer& read, Buffer& write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); #endif + /** + * @brief Wrap existing OpenGL buffer object + * @param id OpenGL buffer ID + * @param targetHint Target hint, see @ref setTargetHint() for more + * information + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL buffer object. + * Unlike buffer created using constructor, the OpenGL object is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Buffer wrap(GLuint id, TargetHint targetHint = TargetHint::Array, ObjectFlags flags = {}) { + return Buffer{id, targetHint, flags}; + } + + /** @overload */ + static Buffer wrap(GLuint id, ObjectFlags flags) { + return Buffer(id, TargetHint::Array, flags); + } + /** * @brief Constructor * @param targetHint Target hint, see @ref setTargetHint() for more @@ -828,7 +850,8 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { * Creates new OpenGL buffer object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the buffer is created on * first use. - * @see @fn_gl{CreateBuffers}, eventually @fn_gl{GenBuffers} + * @see @ref wrap(), @fn_gl{CreateBuffers}, eventually + * @fn_gl{GenBuffers} */ explicit Buffer(TargetHint targetHint = TargetHint::Array); @@ -850,7 +873,7 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { * @brief Destructor * * Deletes associated OpenGL buffer object. - * @see @fn_gl{DeleteBuffers} + * @see @ref wrap(), @ref release(), @fn_gl{DeleteBuffers} */ ~Buffer(); @@ -863,6 +886,16 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { /** @brief OpenGL buffer ID */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL buffer object and returns its ID so it + * is not deleted on destruction. The internal state is then equivalent + * to moved-from state. + * @see @ref wrap() + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Buffer label @@ -1294,6 +1327,8 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { #endif #endif + explicit Buffer(GLuint id, TargetHint targetHint, ObjectFlags flags) noexcept: _id{id}, _targetHint{targetHint}, _flags{flags} {} + void MAGNUM_LOCAL createImplementationDefault(); #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL createImplementationDSA(); @@ -1374,7 +1409,7 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { #ifdef CORRADE_TARGET_NACL void* _mappedBuffer; #endif - bool _created; /* see createIfNotAlready() for details */ + ObjectFlags _flags; }; #ifndef MAGNUM_TARGET_WEBGL @@ -1393,7 +1428,7 @@ inline Buffer::Buffer(Buffer&& other) noexcept: _id{other._id}, _targetHint{othe #ifdef CORRADE_TARGET_NACL _mappedBuffer{other._mappedBuffer}, #endif - _created{other._created} + _flags{other._flags} { other._id = 0; #ifdef CORRADE_TARGET_NACL @@ -1408,10 +1443,16 @@ inline Buffer& Buffer::operator=(Buffer&& other) noexcept { #ifdef CORRADE_TARGET_NACL swap(_mappedBuffer, other._mappedBuffer); #endif - swap(_created, other._created); + swap(_flags, other._flags); return *this; } +inline GLuint Buffer::release() { + const GLuint id = _id; + _id = 0; + return id; +} + #ifndef MAGNUM_TARGET_GLES template Containers::Array inline Buffer::data() { const Int bufferSize = size(); diff --git a/src/Magnum/BufferTexture.h b/src/Magnum/BufferTexture.h index bd5bb77e8..5a56ca6a5 100644 --- a/src/Magnum/BufferTexture.h +++ b/src/Magnum/BufferTexture.h @@ -223,14 +223,29 @@ class MAGNUM_EXPORT BufferTexture: public AbstractTexture { */ static Int offsetAlignment(); + /** + * @brief Wrap existing OpenGL buffer texture object + * @param id OpenGL buffer texture ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_BUFFER}. Unlike texture created using + * constructor, the OpenGL object is by default not deleted on + * destruction, use @p flags for different behavior. + * @see @ref release() + */ + static BufferTexture wrap(GLuint id, ObjectFlags flags = {}) { + return BufferTexture{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_BUFFER}, eventually - * @fn_gl{GenTextures} + * @see @ref wrap(), @fn_gl{CreateTextures} with @def_gl{TEXTURE_BUFFER}, + * eventually @fn_gl{GenTextures} */ explicit BufferTexture(): AbstractTexture(GL_TEXTURE_BUFFER) {} @@ -288,6 +303,8 @@ class MAGNUM_EXPORT BufferTexture: public AbstractTexture { #endif private: + explicit BufferTexture(GLuint id, ObjectFlags flags): AbstractTexture{id, GL_TEXTURE_BUFFER, flags} {} + void MAGNUM_LOCAL setBufferImplementationDefault(BufferTextureFormat internalFormat, Buffer& buffer); void MAGNUM_LOCAL setBufferImplementationDSA(BufferTextureFormat internalFormat, Buffer& buffer); void MAGNUM_LOCAL setBufferImplementationDSAEXT(BufferTextureFormat internalFormat, Buffer& buffer); diff --git a/src/Magnum/CubeMapTexture.cpp b/src/Magnum/CubeMapTexture.cpp index 6753d24c6..f8f64de0e 100644 --- a/src/Magnum/CubeMapTexture.cpp +++ b/src/Magnum/CubeMapTexture.cpp @@ -171,7 +171,7 @@ Vector2i CubeMapTexture::getImageSizeImplementationDSA(const Int level) { } Vector2i CubeMapTexture::getImageSizeImplementationDSAEXT(const Int level) { - _created = true; + _flags |= ObjectFlag::Created; Vector2i size; glGetTextureLevelParameterivEXT(_id, GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, GL_TEXTURE_WIDTH, &size.x()); glGetTextureLevelParameterivEXT(_id, GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, GL_TEXTURE_HEIGHT, &size.y()); @@ -191,7 +191,7 @@ void CubeMapTexture::getImageImplementationDSA(const Coordinate coordinate, cons } void CubeMapTexture::getImageImplementationDSAEXT(const Coordinate coordinate, const GLint level, const Vector2i&, const ColorFormat format, const ColorType type, std::size_t, GLvoid* const data) { - _created = true; + _flags |= ObjectFlag::Created; glGetTextureImageEXT(_id, GLenum(coordinate), level, GLenum(format), GLenum(type), data); } @@ -212,7 +212,7 @@ void CubeMapTexture::subImageImplementationDSA(const Coordinate coordinate, cons } void CubeMapTexture::subImageImplementationDSAEXT(const Coordinate coordinate, const GLint level, const Vector2i& offset, const Vector2i& size, const ColorFormat format, const ColorType type, const GLvoid* const data) { - _created = true; + _flags |= ObjectFlag::Created; glTextureSubImage2DEXT(_id, GLenum(coordinate), level, offset.x(), offset.y(), size.x(), size.y(), GLenum(format), GLenum(type), data); } #endif diff --git a/src/Magnum/CubeMapTexture.h b/src/Magnum/CubeMapTexture.h index cd8d845eb..201513b59 100644 --- a/src/Magnum/CubeMapTexture.h +++ b/src/Magnum/CubeMapTexture.h @@ -102,14 +102,29 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { */ static Vector2i maxSize(); + /** + * @brief Wrap existing OpenGL cube map texture object + * @param id OpenGL cube map texture ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_CUBE_MAP}. Unlike texture created using + * constructor, the OpenGL object is by default not deleted on + * destruction, use @p flags for different behavior. + * @see @ref release() + */ + static CubeMapTexture wrap(GLuint id, ObjectFlags flags = {}) { + return CubeMapTexture{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_CUBE_MAP}, - * eventually @fn_gl{GenTextures} + * @see @ref wrap(), @fn_gl{CreateTextures} with + * @def_gl{TEXTURE_CUBE_MAP}, eventually @fn_gl{GenTextures} */ explicit CubeMapTexture(): AbstractTexture(GL_TEXTURE_CUBE_MAP) {} @@ -692,6 +707,8 @@ class MAGNUM_EXPORT CubeMapTexture: public AbstractTexture { #endif private: + explicit CubeMapTexture(GLuint id, ObjectFlags flags) noexcept: AbstractTexture{id, GL_TEXTURE_CUBE_MAP, flags} {} + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) Vector2i MAGNUM_LOCAL getImageSizeImplementationDefault(Int level); #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/CubeMapTextureArray.h b/src/Magnum/CubeMapTextureArray.h index 28b760461..a104fdef2 100644 --- a/src/Magnum/CubeMapTextureArray.h +++ b/src/Magnum/CubeMapTextureArray.h @@ -95,14 +95,29 @@ class MAGNUM_EXPORT CubeMapTextureArray: public AbstractTexture { */ static Vector3i maxSize(); + /** + * @brief Wrap existing OpenGL cube map array texture object + * @param id OpenGL cube map array texture ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_CUBE_MAP_ARRAY}. Unlike texture created + * using constructor, the OpenGL object is by default not deleted on + * destruction, use @p flags for different behavior. + * @see @ref release() + */ + static CubeMapTextureArray wrap(GLuint id, ObjectFlags flags = {}) { + return CubeMapTextureArray{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_CUBE_MAP_ARRAY}, - * eventually @fn_gl{GenTextures} + * @see @ref wrap(), @fn_gl{CreateTextures} with + * @def_gl{TEXTURE_CUBE_MAP_ARRAY}, eventually @fn_gl{GenTextures} */ explicit CubeMapTextureArray(): AbstractTexture(GL_TEXTURE_CUBE_MAP_ARRAY) {} @@ -499,6 +514,9 @@ class MAGNUM_EXPORT CubeMapTextureArray: public AbstractTexture { return *this; } #endif + + private: + explicit CubeMapTextureArray(GLuint id, ObjectFlags flags) noexcept: AbstractTexture{id, GL_TEXTURE_CUBE_MAP_ARRAY, flags} {} }; } diff --git a/src/Magnum/DefaultFramebuffer.cpp b/src/Magnum/DefaultFramebuffer.cpp index 649bcac99..b636a7b65 100644 --- a/src/Magnum/DefaultFramebuffer.cpp +++ b/src/Magnum/DefaultFramebuffer.cpp @@ -38,7 +38,7 @@ DefaultFramebuffer defaultFramebuffer; DefaultFramebuffer::DefaultFramebuffer() { _id = 0; - _created = true; + _flags |= ObjectFlag::Created; } DefaultFramebuffer::Status DefaultFramebuffer::checkStatus(const FramebufferTarget target) { diff --git a/src/Magnum/Framebuffer.cpp b/src/Magnum/Framebuffer.cpp index 964b087df..1ea6b7e4c 100644 --- a/src/Magnum/Framebuffer.cpp +++ b/src/Magnum/Framebuffer.cpp @@ -103,19 +103,18 @@ Framebuffer::Framebuffer(const Range2Di& viewport) { void Framebuffer::createImplementationDefault() { glGenFramebuffers(1, &_id); - _created = false; } #ifndef MAGNUM_TARGET_GLES void Framebuffer::createImplementationDSA() { glCreateFramebuffers(1, &_id); - _created = true; + _flags |= ObjectFlag::Created; } #endif Framebuffer::~Framebuffer() { - /* Moved out, nothing to do */ - if(!_id) return; + /* Moved out or not deleting on destruction, nothing to do */ + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; /* If bound, remove itself from state */ Implementation::FramebufferState& state = *Context::current()->state().framebuffer; @@ -305,7 +304,7 @@ void Framebuffer::renderbufferImplementationDSA(const BufferAttachment attachmen } void Framebuffer::renderbufferImplementationDSAEXT(BufferAttachment attachment, Renderbuffer& renderbuffer) { - _created = true; + _flags |= ObjectFlag::Created; glNamedFramebufferRenderbufferEXT(_id, GLenum(attachment), GL_RENDERBUFFER, renderbuffer.id()); } @@ -318,7 +317,7 @@ void Framebuffer::texture1DImplementationDSA(const BufferAttachment attachment, } void Framebuffer::texture1DImplementationDSAEXT(BufferAttachment attachment, GLuint textureId, GLint mipLevel) { - _created = true; + _flags |= ObjectFlag::Created; glNamedFramebufferTexture1DEXT(_id, GLenum(attachment), GL_TEXTURE_1D, textureId, mipLevel); } #endif @@ -333,7 +332,7 @@ void Framebuffer::texture2DImplementationDSA(const BufferAttachment attachment, } void Framebuffer::texture2DImplementationDSAEXT(BufferAttachment attachment, GLenum textureTarget, GLuint textureId, GLint mipLevel) { - _created = true; + _flags |= ObjectFlag::Created; glNamedFramebufferTexture2DEXT(_id, GLenum(attachment), textureTarget, textureId, mipLevel); } #endif @@ -360,7 +359,7 @@ void Framebuffer::textureLayerImplementationDSA(const BufferAttachment attachmen } void Framebuffer::textureLayerImplementationDSAEXT(BufferAttachment attachment, GLuint textureId, GLint mipLevel, GLint layer) { - _created = true; + _flags |= ObjectFlag::Created; glNamedFramebufferTextureLayerEXT(_id, GLenum(attachment), textureId, mipLevel, layer); } #endif diff --git a/src/Magnum/Framebuffer.h b/src/Magnum/Framebuffer.h index 95523eea0..9aa5b5c04 100644 --- a/src/Magnum/Framebuffer.h +++ b/src/Magnum/Framebuffer.h @@ -318,14 +318,30 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer, public AbstractObje */ static Int maxColorAttachments(); + /** + * @brief Wrap existing OpenGL framebuffer object + * @param id OpenGL framebuffer ID + * @param viewport Viewport to use with this framebuffer + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL framebuffer + * object. Unlike framebuffer created using constructor, the OpenGL + * object is by default not deleted on destruction, use @p flags for + * different behavior. + * @see @ref release() + */ + static Framebuffer wrap(GLuint id, const Range2Di& viewport, ObjectFlags flags = {}) { + return Framebuffer{id, viewport, flags}; + } + /** * @brief Constructor * * Generates new OpenGL framebuffer object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the framebuffer is created on * first use. - * @see @ref setViewport(), @fn_gl{CreateFramebuffers}, eventually - * @fn_gl{GenFramebuffers} + * @see @ref setViewport(), @ref wrap(), @fn_gl{CreateFramebuffers}, + * eventually @fn_gl{GenFramebuffers} */ explicit Framebuffer(const Range2Di& viewport); @@ -339,7 +355,7 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer, public AbstractObje * @brief Destructor * * Deletes associated OpenGL framebuffer object. - * @see @fn_gl{DeleteFramebuffers} + * @see @ref wrap(), @ref release(), @fn_gl{DeleteFramebuffers} */ ~Framebuffer(); @@ -352,6 +368,16 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer, public AbstractObje /** @brief OpenGL framebuffer ID */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL framebuffer object and returns its ID + * so it is not deleted on destruction. The internal state is then + * equivalent to moved-from state. + * @see @ref wrap() + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Framebuffer label @@ -716,6 +742,8 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer, public AbstractObje #endif private: + explicit Framebuffer(GLuint id, const Range2Di& viewport, ObjectFlags flags) noexcept: AbstractFramebuffer{id, viewport, flags} {} + void MAGNUM_LOCAL createImplementationDefault(); #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL createImplementationDSA(); @@ -758,7 +786,7 @@ Debug MAGNUM_EXPORT operator<<(Debug debug, Framebuffer::Status value); inline Framebuffer::Framebuffer(Framebuffer&& other) noexcept { _id = other._id; _viewport = other._viewport; - _created = other._created; + _flags = other._flags; other._id = 0; other._viewport = {}; } @@ -767,10 +795,16 @@ inline Framebuffer& Framebuffer::operator=(Framebuffer&& other) noexcept { using std::swap; swap(_id, other._id); swap(_viewport, other._viewport); - swap(_created, other._created); + swap(_flags, other._flags); return *this; } +inline GLuint Framebuffer::release() { + const GLuint id = _id; + _id = 0; + return id; +} + } #endif diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h index 610019ecd..636a95fb1 100644 --- a/src/Magnum/Magnum.h +++ b/src/Magnum/Magnum.h @@ -494,6 +494,8 @@ typedef MultisampleTexture<3> MultisampleTexture2DArray; #endif #endif +/* ObjectFlag, ObjectFlags are used only in conjunction with *::wrap() function */ + class PrimitiveQuery; class SampleQuery; class TimeQuery; diff --git a/src/Magnum/Mesh.cpp b/src/Magnum/Mesh.cpp index f74d1d784..15c682d32 100644 --- a/src/Magnum/Mesh.cpp +++ b/src/Magnum/Mesh.cpp @@ -107,7 +107,7 @@ std::size_t Mesh::indexSize(IndexType type) { CORRADE_ASSERT_UNREACHABLE(); } -Mesh::Mesh(const MeshPrimitive primitive): _primitive{primitive}, _count{0}, _baseVertex{0}, _instanceCount{1}, +Mesh::Mesh(const MeshPrimitive primitive): _primitive{primitive}, _flags{ObjectFlag::DeleteOnDestruction}, _count{0}, _baseVertex{0}, _instanceCount{1}, #ifndef MAGNUM_TARGET_GLES _baseInstance{0}, #endif @@ -120,8 +120,8 @@ Mesh::Mesh(const MeshPrimitive primitive): _primitive{primitive}, _count{0}, _ba } Mesh::~Mesh() { - /* Moved out, nothing to do */ - if(!_id) return; + /* Moved out or not deleting on destruction, nothing to do */ + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; /* Remove current vao from the state */ GLuint& current = Context::current()->state().mesh->currentVAO; @@ -130,7 +130,7 @@ Mesh::~Mesh() { (this->*Context::current()->state().mesh->destroyImplementation)(); } -Mesh::Mesh(Mesh&& other) noexcept: _id(other._id), _created{other._created}, _primitive(other._primitive), _count(other._count), _baseVertex{other._baseVertex}, _instanceCount{other._instanceCount}, +Mesh::Mesh(Mesh&& other) noexcept: _id(other._id), _primitive(other._primitive), _flags{other._flags}, _count(other._count), _baseVertex{other._baseVertex}, _instanceCount{other._instanceCount}, #ifndef MAGNUM_TARGET_GLES _baseInstance{other._baseInstance}, #endif @@ -151,7 +151,7 @@ Mesh::Mesh(Mesh&& other) noexcept: _id(other._id), _created{other._created}, _pr Mesh& Mesh::operator=(Mesh&& other) noexcept { using std::swap; swap(_id, other._id); - swap(_created, other._created); + swap(_flags, other._flags); swap(_primitive, other._primitive); swap(_count, other._count); swap(_baseVertex, other._baseVertex); @@ -178,15 +178,15 @@ Mesh& Mesh::operator=(Mesh&& other) noexcept { } inline void Mesh::createIfNotAlready() { - /* If VAO extension is not available, _created is always true */ - if(_created) return; + /* If VAO extension is not available, the following is always true */ + if(_flags & ObjectFlag::Created) return; /* glGen*() does not create the object, just reserves the name. Some commands (such as glObjectLabel()) operate with IDs directly and they require the object to be created. Binding the VAO finally creates it. Also all EXT DSA functions implicitly create it. */ bindVAO(); - CORRADE_INTERNAL_ASSERT(_created); + CORRADE_INTERNAL_ASSERT(_flags & ObjectFlag::Created); } #ifndef MAGNUM_TARGET_WEBGL @@ -353,7 +353,7 @@ void Mesh::bindVAO() { GLuint& current = Context::current()->state().mesh->currentVAO; if(current != _id) { /* Binding the VAO finally creates it */ - _created = true; + _flags |= ObjectFlag::Created; #ifndef MAGNUM_TARGET_GLES2 glBindVertexArray(current = _id); #elif !defined(CORRADE_TARGET_NACL) @@ -366,7 +366,7 @@ void Mesh::bindVAO() { void Mesh::createImplementationDefault() { _id = 0; - _created = true; + _flags |= ObjectFlag::Created; } void Mesh::createImplementationVAO() { @@ -377,14 +377,13 @@ void Mesh::createImplementationVAO() { #else CORRADE_ASSERT_UNREACHABLE(); #endif - _created = false; CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding); } #ifndef MAGNUM_TARGET_GLES void Mesh::createImplementationVAODSA() { glCreateVertexArrays(1, &_id); - _created = true; + _flags |= ObjectFlag::Created; } #endif @@ -425,7 +424,7 @@ void Mesh::attributePointerImplementationVAO(const GenericAttribute& attribute) #ifndef MAGNUM_TARGET_GLES void Mesh::attributePointerImplementationDSAEXT(const GenericAttribute& attribute) { - _created = true; + _flags |= ObjectFlag::Created; glEnableVertexArrayAttribEXT(_id, attribute.location); glVertexArrayVertexAttribOffsetEXT(_id, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.offset); if(attribute.divisor) @@ -462,7 +461,7 @@ void Mesh::attributePointerImplementationVAO(const IntegerAttribute& attribute) #ifndef MAGNUM_TARGET_GLES void Mesh::attributePointerImplementationDSAEXT(const IntegerAttribute& attribute) { - _created = true; + _flags |= ObjectFlag::Created; glEnableVertexArrayAttribEXT(_id, attribute.location); glVertexArrayVertexAttribIOffsetEXT(_id, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.stride, attribute.offset); if(attribute.divisor) @@ -493,7 +492,7 @@ void Mesh::attributePointerImplementationVAO(const LongAttribute& attribute) { } void Mesh::attributePointerImplementationDSAEXT(const LongAttribute& attribute) { - _created = true; + _flags |= ObjectFlag::Created; glEnableVertexArrayAttribEXT(_id, attribute.location); glVertexArrayVertexAttribLOffsetEXT(_id, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.stride, attribute.offset); if(attribute.divisor) diff --git a/src/Magnum/Mesh.h b/src/Magnum/Mesh.h index bf26e3a38..6301eaac3 100644 --- a/src/Magnum/Mesh.h +++ b/src/Magnum/Mesh.h @@ -425,6 +425,32 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { */ static std::size_t indexSize(IndexType type); + /** + * @brief Wrap existing OpenGL vertex array object + * @param id OpenGL vertex array ID + * @param primitive Primitive type + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL vertex array + * object. Unlike vertex array created using constructor, the OpenGL + * object is by default not deleted on destruction, use @p flags for + * different behavior. + * @see @ref release() + * @requires_gl30 Extension @extension{ARB,vertex_array_object} + * @requires_gles30 Extension @es_extension{OES,vertex_array_object} in + * OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{OES,vertex_array_object} + * in WebGL 1.0. + */ + static Mesh wrap(GLuint id, MeshPrimitive primitive = MeshPrimitive::Triangles, ObjectFlags flags = {}) { + return Mesh{id, primitive, flags}; + } + + /** @overload */ + static Mesh wrap(GLuint id, ObjectFlags flags) { + return wrap(id, MeshPrimitive::Triangles, flags); + } + /** * @brief Constructor * @param primitive Primitive type @@ -435,8 +461,8 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { * available, vertex array object is created. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the vertex array object is * created on first use. - * @see @ref setPrimitive(), @ref setCount(), @fn_gl{CreateVertexArrays}, - * eventually @fn_gl{GenVertexArrays} + * @see @ref setPrimitive(), @ref setCount(), @ref wrap(), + * @fn_gl{CreateVertexArrays}, eventually @fn_gl{GenVertexArrays} */ explicit Mesh(MeshPrimitive primitive = MeshPrimitive::Triangles); @@ -453,7 +479,7 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { * ES 3.0, WebGL 2.0, @es_extension{OES,vertex_array_object} in OpenGL * ES 2.0 or @webgl_extension{OES,vertex_array_object} in WebGL 1.0 is * available, associated vertex array object is deleted. - * @see @fn_gl{DeleteVertexArrays} + * @see @ref wrap(), @ref release(), @fn_gl{DeleteVertexArrays} */ ~Mesh(); @@ -473,6 +499,21 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL vertex array object and returns its ID + * so it is not deleted on destruction. The internal state is then + * equivalent to moved-from state. + * @see @ref wrap() + * @requires_gl30 Extension @extension{ARB,vertex_array_object} + * @requires_gles30 Extension @es_extension{OES,vertex_array_object} in + * OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{OES,vertex_array_object} + * in WebGL 1.0. + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Mesh label @@ -887,6 +928,8 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { #endif #endif + explicit Mesh(GLuint id, MeshPrimitive primitive, ObjectFlags flags): _id{id}, _primitive{primitive}, _flags{flags} {} + void MAGNUM_LOCAL createIfNotAlready(); #ifndef MAGNUM_TARGET_WEBGL @@ -1038,8 +1081,8 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { #endif GLuint _id; - bool _created; /* see createIfNotAlready() for details */ MeshPrimitive _primitive; + ObjectFlags _flags; Int _count, _baseVertex, _instanceCount; #ifndef MAGNUM_TARGET_GLES UnsignedInt _baseInstance; @@ -1066,6 +1109,12 @@ Debug MAGNUM_EXPORT operator<<(Debug debug, MeshPrimitive value); /** @debugoperatorclassenum{Magnum::Mesh,Magnum::Mesh::IndexType} */ Debug MAGNUM_EXPORT operator<<(Debug debug, Mesh::IndexType value); +inline GLuint Mesh::release() { + const GLuint id = _id; + _id = 0; + return id; +} + } namespace Corrade { namespace Utility { diff --git a/src/Magnum/MultisampleTexture.h b/src/Magnum/MultisampleTexture.h index f0bd5732f..40b3643ba 100644 --- a/src/Magnum/MultisampleTexture.h +++ b/src/Magnum/MultisampleTexture.h @@ -111,13 +111,31 @@ template class MultisampleTexture: public AbstractTextur return Implementation::maxMultisampleTextureSize(); } + /** + * @brief Wrap existing OpenGL multisample texture object + * @param id OpenGL multisample texture ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_2D_MULTISAMPLE} or + * @def_gl{TEXTURE_2D_MULTISAMPLE_ARRAY} based on dimension count. + * Unlike texture created using constructor, the OpenGL object is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static MultisampleTexture wrap(GLuint id, ObjectFlags flags = {}) { + return MultisampleTexture{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_2D_MULTISAMPLE} or + * @see @ref wrap(), @fn_gl{CreateTextures} with + * @def_gl{TEXTURE_2D_MULTISAMPLE} or * @def_gl{TEXTURE_2D_MULTISAMPLE_ARRAY}, eventually * @fn_gl{GenTextures} */ @@ -205,6 +223,9 @@ template class MultisampleTexture: public AbstractTextur return *this; } #endif + + private: + explicit MultisampleTexture(GLuint id, ObjectFlags flags): AbstractTexture{id, Implementation::multisampleTextureTarget(), flags} {} }; /** diff --git a/src/Magnum/PrimitiveQuery.h b/src/Magnum/PrimitiveQuery.h index eb3fdf40d..615e62378 100644 --- a/src/Magnum/PrimitiveQuery.h +++ b/src/Magnum/PrimitiveQuery.h @@ -84,6 +84,22 @@ class PrimitiveQuery: public AbstractQuery { TransformFeedbackPrimitivesWritten = GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN }; + /** + * @brief Wrap existing OpenGL primitive query object + * @param id OpenGL primitive query ID + * @param target Query target + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL query object. + * Unlike query created using constructor, the OpenGL object is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static PrimitiveQuery wrap(GLuint id, Target target, ObjectFlags flags = {}) { + return PrimitiveQuery{id, target, flags}; + } + #ifdef MAGNUM_BUILD_DEPRECATED /** * @copybrief PrimitiveQuery(Target) @@ -98,7 +114,7 @@ class PrimitiveQuery: public AbstractQuery { * Creates new OpenGL query object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the query is created on first * use. - * @see @fn_gl{CreateQueries}, eventually @fn_gl{GenQueries} + * @see @ref wrap(), @fn_gl{CreateQueries}, eventually @fn_gl{GenQueries} */ explicit PrimitiveQuery(Target target): AbstractQuery(GLenum(target)) {} @@ -125,6 +141,9 @@ class PrimitiveQuery: public AbstractQuery { return *this; } #endif + + private: + explicit PrimitiveQuery(GLuint id, Target target, ObjectFlags flags) noexcept: AbstractQuery{id, GLenum(target), flags} {} }; } diff --git a/src/Magnum/RectangleTexture.h b/src/Magnum/RectangleTexture.h index 72c518243..19340002a 100644 --- a/src/Magnum/RectangleTexture.h +++ b/src/Magnum/RectangleTexture.h @@ -80,14 +80,29 @@ class MAGNUM_EXPORT RectangleTexture: public AbstractTexture { */ static Vector2i maxSize(); + /** + * @brief Wrap existing OpenGL rectangle texture object + * @param id OpenGL rectangle texture ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_RECTANGLE}. Unlike texture created using + * constructor, the OpenGL object is by default not deleted on + * destruction, use @p flags for different behavior. + * @see @ref release() + */ + static RectangleTexture wrap(GLuint id, ObjectFlags flags = {}) { + return RectangleTexture{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_RECTANGLE}, - * eventually @fn_gl{GenTextures} + * @see @ref wrap(), @fn_gl{CreateTextures} with + * @def_gl{TEXTURE_RECTANGLE}, eventually @fn_gl{GenTextures} */ explicit RectangleTexture(): AbstractTexture(GL_TEXTURE_RECTANGLE) {} @@ -410,6 +425,9 @@ class MAGNUM_EXPORT RectangleTexture: public AbstractTexture { return *this; } #endif + + private: + explicit RectangleTexture(GLuint id, ObjectFlags flags) noexcept: AbstractTexture{id, GL_TEXTURE_RECTANGLE, flags} {} }; } diff --git a/src/Magnum/Renderbuffer.cpp b/src/Magnum/Renderbuffer.cpp index 6e82490e9..9544dae19 100644 --- a/src/Magnum/Renderbuffer.cpp +++ b/src/Magnum/Renderbuffer.cpp @@ -68,25 +68,24 @@ Int Renderbuffer::maxSamples() { } #endif -Renderbuffer::Renderbuffer() { +Renderbuffer::Renderbuffer(): _flags{ObjectFlag::DeleteOnDestruction} { (this->*Context::current()->state().framebuffer->createRenderbufferImplementation)(); } void Renderbuffer::createImplementationDefault() { glGenRenderbuffers(1, &_id); - _created = false; } #ifndef MAGNUM_TARGET_GLES void Renderbuffer::createImplementationDSA() { glCreateRenderbuffers(1, &_id); - _created = true; + _flags |= ObjectFlag::Created; } #endif Renderbuffer::~Renderbuffer() { /* Moved out, nothing to do */ - if(!_id) return; + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; /* If bound, remove itself from state */ GLuint& binding = Context::current()->state().framebuffer->renderbufferBinding; @@ -96,14 +95,14 @@ Renderbuffer::~Renderbuffer() { } inline void Renderbuffer::createIfNotAlready() { - if(_created) return; + if(_flags & ObjectFlag::Created) return; /* glGen*() does not create the object, just reserves the name. Some commands (such as glObjectLabel()) operate with IDs directly and they require the object to be created. Binding the renderbuffer finally creates it. Also all EXT DSA functions implicitly create it. */ bind(); - CORRADE_INTERNAL_ASSERT(_created); + CORRADE_INTERNAL_ASSERT(_flags & ObjectFlag::Created); } #ifndef MAGNUM_TARGET_WEBGL @@ -136,7 +135,7 @@ void Renderbuffer::bind() { /* Binding the renderbuffer finally creates it */ binding = _id; - _created = true; + _flags |= ObjectFlag::Created; glBindRenderbuffer(GL_RENDERBUFFER, _id); } @@ -151,7 +150,7 @@ void Renderbuffer::storageImplementationDSA(const RenderbufferFormat internalFor } void Renderbuffer::storageImplementationDSAEXT(RenderbufferFormat internalFormat, const Vector2i& size) { - _created = true; + _flags |= ObjectFlag::Created; glNamedRenderbufferStorageEXT(_id, GLenum(internalFormat), size.x(), size.y()); } #endif @@ -193,7 +192,7 @@ void Renderbuffer::storageMultisampleImplementationDSA(const GLsizei samples, co } void Renderbuffer::storageMultisampleImplementationDSAEXT(GLsizei samples, RenderbufferFormat internalFormat, const Vector2i& size) { - _created = true; + _flags |= ObjectFlag::Created; glNamedRenderbufferStorageMultisampleEXT(_id, samples, GLenum(internalFormat), size.x(), size.y()); } #endif diff --git a/src/Magnum/Renderbuffer.h b/src/Magnum/Renderbuffer.h index 927f8e260..849af7a9a 100644 --- a/src/Magnum/Renderbuffer.h +++ b/src/Magnum/Renderbuffer.h @@ -88,13 +88,29 @@ class MAGNUM_EXPORT Renderbuffer: public AbstractObject { static Int maxSamples(); #endif + /** + * @brief Wrap existing OpenGL renderbuffer object + * @param id OpenGL renderbuffer ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL renderbuffer + * object. Unlike renderbuffer created using constructor, the OpenGL + * object is by default not deleted on destruction, use @p flags for + * different behavior. + * @see @ref release() + */ + static Renderbuffer wrap(GLuint id, ObjectFlags flags = {}) { + return Renderbuffer{id, flags}; + } + /** * @brief Constructor * * Generates new OpenGL renderbuffer object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the renderbuffer is created * on first use. - * @see @fn_gl{CreateRenderbuffers}, eventually @fn_gl{GenRenderbuffers} + * @see @ref wrap(), @fn_gl{CreateRenderbuffers}, eventually + * @fn_gl{GenRenderbuffers} */ explicit Renderbuffer(); @@ -108,7 +124,7 @@ class MAGNUM_EXPORT Renderbuffer: public AbstractObject { * @brief Destructor * * Deletes associated OpenGL renderbuffer object. - * @see @fn_gl{DeleteRenderbuffers} + * @see @ref wrap(), @ref release(), @fn_gl{DeleteRenderbuffers} */ ~Renderbuffer(); @@ -121,6 +137,16 @@ class MAGNUM_EXPORT Renderbuffer: public AbstractObject { /** @brief OpenGL renderbuffer ID */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL renderbuffer object and returns its ID + * so it is not deleted on destruction. The internal state is then + * equivalent to moved-from state. + * @see @ref wrap() + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Renderbuffer label @@ -199,6 +225,8 @@ class MAGNUM_EXPORT Renderbuffer: public AbstractObject { #endif private: + explicit Renderbuffer(GLuint id, ObjectFlags flags) noexcept: _id{id}, _flags{flags} {} + void MAGNUM_LOCAL createImplementationDefault(); #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL createImplementationDSA(); @@ -230,20 +258,26 @@ class MAGNUM_EXPORT Renderbuffer: public AbstractObject { void MAGNUM_LOCAL bind(); GLuint _id; - bool _created; /* see createIfNotAlready() for details */ + ObjectFlags _flags; }; -inline Renderbuffer::Renderbuffer(Renderbuffer&& other) noexcept: _id{other._id}, _created{other._created} { +inline Renderbuffer::Renderbuffer(Renderbuffer&& other) noexcept: _id{other._id}, _flags{other._flags} { other._id = 0; } inline Renderbuffer& Renderbuffer::operator=(Renderbuffer&& other) noexcept { using std::swap; swap(_id, other._id); - swap(_created, other._created); + swap(_flags, other._flags); return *this; } +inline GLuint Renderbuffer::release() { + const GLuint id = _id; + _id = 0; + return id; +} + } #endif diff --git a/src/Magnum/SampleQuery.h b/src/Magnum/SampleQuery.h index 2fe71d2bc..7fe518d5f 100644 --- a/src/Magnum/SampleQuery.h +++ b/src/Magnum/SampleQuery.h @@ -182,6 +182,21 @@ class SampleQuery: public AbstractQuery { }; #endif + /** + * @brief Wrap existing OpenGL sample query object + * @param id OpenGL sample query ID + * @param target Query target + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL query object. + * Unlike query created using constructor, the OpenGL object is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release(), @fn_gl{IsQuery} + */ + static SampleQuery wrap(GLuint id, Target target, ObjectFlags flags = {}) { + return SampleQuery{id, target, flags}; + } #ifdef MAGNUM_BUILD_DEPRECATED /** @@ -197,7 +212,7 @@ class SampleQuery: public AbstractQuery { * Creates new OpenGL query object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the query is created on first * use. - * @see @fn_gl{CreateQueries}, eventually @fn_gl{GenQueries} + * @see @ref wrap(), @fn_gl{CreateQueries}, eventually @fn_gl{GenQueries} */ explicit SampleQuery(Target target): AbstractQuery(GLenum(target)) {} @@ -250,6 +265,9 @@ class SampleQuery: public AbstractQuery { return *this; } #endif + + private: + explicit SampleQuery(GLuint id, Target target, ObjectFlags flags) noexcept: AbstractQuery{id, GLenum(target), flags} {} }; } diff --git a/src/Magnum/Test/BufferGLTest.cpp b/src/Magnum/Test/BufferGLTest.cpp index 04ace7a4d..476728ce3 100644 --- a/src/Magnum/Test/BufferGLTest.cpp +++ b/src/Magnum/Test/BufferGLTest.cpp @@ -40,6 +40,7 @@ struct BufferGLTest: AbstractOpenGLTester { void construct(); void constructCopy(); void constructMove(); + void wrap(); void label(); @@ -65,6 +66,7 @@ BufferGLTest::BufferGLTest() { addTests({&BufferGLTest::construct, &BufferGLTest::constructCopy, &BufferGLTest::constructMove, + &BufferGLTest::wrap, &BufferGLTest::label, @@ -126,6 +128,21 @@ void BufferGLTest::constructMove() { CORRADE_COMPARE(c.id(), id); } +void BufferGLTest::wrap() { + GLuint id; + glGenBuffers(1, &id); + + /* Releasing won't delete anything */ + { + auto buffer = Buffer::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(buffer.release(), id); + } + + /* ...so we can wrap it again */ + Buffer::wrap(id); + glDeleteBuffers(1, &id); +} + void BufferGLTest::label() { /* No-Op version is tested in AbstractObjectGLTest */ if(!Context::current()->isExtensionSupported() && diff --git a/src/Magnum/Test/BufferTextureGLTest.cpp b/src/Magnum/Test/BufferTextureGLTest.cpp index 93f9909c9..6914f1580 100644 --- a/src/Magnum/Test/BufferTextureGLTest.cpp +++ b/src/Magnum/Test/BufferTextureGLTest.cpp @@ -35,6 +35,8 @@ struct BufferTextureGLTest: AbstractOpenGLTester { explicit BufferTextureGLTest(); void construct(); + void wrap(); + void bind(); void setBuffer(); void setBufferOffset(); @@ -42,6 +44,8 @@ struct BufferTextureGLTest: AbstractOpenGLTester { BufferTextureGLTest::BufferTextureGLTest() { addTests({&BufferTextureGLTest::construct, + &BufferTextureGLTest::wrap, + &BufferTextureGLTest::bind, &BufferTextureGLTest::setBuffer, &BufferTextureGLTest::setBufferOffset}); @@ -61,6 +65,24 @@ void BufferTextureGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void BufferTextureGLTest::wrap() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_buffer_object::string() + std::string(" is not supported.")); + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = BufferTexture::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + BufferTexture::wrap(id); + glDeleteTextures(1, &id); +} + void BufferTextureGLTest::bind() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_buffer_object::string() + std::string(" is not supported.")); diff --git a/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp b/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp index e598d072d..ceaa84182 100644 --- a/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp +++ b/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp @@ -40,6 +40,8 @@ struct CubeMapTextureArrayGLTest: AbstractOpenGLTester { explicit CubeMapTextureArrayGLTest(); void construct(); + void wrap(); + void bind(); void sampling(); @@ -65,6 +67,8 @@ struct CubeMapTextureArrayGLTest: AbstractOpenGLTester { CubeMapTextureArrayGLTest::CubeMapTextureArrayGLTest() { addTests({&CubeMapTextureArrayGLTest::construct, + &CubeMapTextureArrayGLTest::wrap, + &CubeMapTextureArrayGLTest::bind, &CubeMapTextureArrayGLTest::sampling, @@ -102,6 +106,24 @@ void CubeMapTextureArrayGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void CubeMapTextureArrayGLTest::wrap() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = CubeMapTextureArray::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + CubeMapTextureArray::wrap(id); + glDeleteTextures(1, &id); +} + void CubeMapTextureArrayGLTest::bind() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); diff --git a/src/Magnum/Test/CubeMapTextureGLTest.cpp b/src/Magnum/Test/CubeMapTextureGLTest.cpp index 740960a0a..78fda7839 100644 --- a/src/Magnum/Test/CubeMapTextureGLTest.cpp +++ b/src/Magnum/Test/CubeMapTextureGLTest.cpp @@ -43,6 +43,8 @@ struct CubeMapTextureGLTest: AbstractOpenGLTester { explicit CubeMapTextureGLTest(); void construct(); + void wrap(); + void bind(); void sampling(); @@ -88,6 +90,8 @@ struct CubeMapTextureGLTest: AbstractOpenGLTester { CubeMapTextureGLTest::CubeMapTextureGLTest() { addTests({&CubeMapTextureGLTest::construct, + &CubeMapTextureGLTest::wrap, + &CubeMapTextureGLTest::bind, &CubeMapTextureGLTest::sampling, @@ -142,6 +146,21 @@ void CubeMapTextureGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void CubeMapTextureGLTest::wrap() { + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = CubeMapTexture::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + CubeMapTexture::wrap(id); + glDeleteTextures(1, &id); +} + void CubeMapTextureGLTest::bind() { CubeMapTexture texture; texture.bind(15); diff --git a/src/Magnum/Test/FramebufferGLTest.cpp b/src/Magnum/Test/FramebufferGLTest.cpp index 4c31f937a..f22103246 100644 --- a/src/Magnum/Test/FramebufferGLTest.cpp +++ b/src/Magnum/Test/FramebufferGLTest.cpp @@ -55,6 +55,7 @@ struct FramebufferGLTest: AbstractOpenGLTester { void construct(); void constructCopy(); void constructMove(); + void wrap(); void label(); @@ -105,6 +106,7 @@ FramebufferGLTest::FramebufferGLTest() { addTests({&FramebufferGLTest::construct, &FramebufferGLTest::constructCopy, &FramebufferGLTest::constructMove, + &FramebufferGLTest::wrap, &FramebufferGLTest::label, @@ -207,6 +209,26 @@ void FramebufferGLTest::constructMove() { CORRADE_COMPARE(c.viewport(), Range2Di({32, 16}, {128, 256})); } +void FramebufferGLTest::wrap() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::framebuffer_object::string() + std::string(" is not available.")); + #endif + + GLuint id; + glGenFramebuffers(1, &id); + + /* Releasing won't delete anything */ + { + auto framebuffer = Framebuffer::wrap(id, {}, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(framebuffer.release(), id); + } + + /* ...so we can wrap it again */ + Framebuffer::wrap(id, {}); + glDeleteFramebuffers(1, &id); +} + void FramebufferGLTest::label() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Test/MeshGLTest.cpp b/src/Magnum/Test/MeshGLTest.cpp index 19db3e587..870308d88 100644 --- a/src/Magnum/Test/MeshGLTest.cpp +++ b/src/Magnum/Test/MeshGLTest.cpp @@ -48,6 +48,7 @@ struct MeshGLTest: AbstractOpenGLTester { void construct(); void constructCopy(); void constructMove(); + void wrap(); void label(); @@ -139,6 +140,7 @@ MeshGLTest::MeshGLTest() { addTests({&MeshGLTest::construct, &MeshGLTest::constructCopy, &MeshGLTest::constructMove, + &MeshGLTest::wrap, &MeshGLTest::label, @@ -289,6 +291,30 @@ void MeshGLTest::constructMove() { CORRADE_COMPARE(c.id(), id); } +void MeshGLTest::wrap() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + #elif defined(MAGNUM_TARGET_GLES2) + if(!Context::current()->isExtensionSupported()) + #endif + { + CORRADE_SKIP(Extensions::GL::ARB::vertex_array_object::string() + std::string{" is not supported."}); + } + + GLuint id; + glGenVertexArrays(1, &id); + + /* Releasing won't delete anything */ + { + auto mesh = Mesh::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(mesh.release(), id); + } + + /* ...so we can wrap it again */ + Mesh::wrap(id); + glDeleteVertexArrays(1, &id); +} + void MeshGLTest::label() { /* No-Op version is tested in AbstractObjectGLTest */ if(!Context::current()->isExtensionSupported() && diff --git a/src/Magnum/Test/MultisampleTextureGLTest.cpp b/src/Magnum/Test/MultisampleTextureGLTest.cpp index 2c5c3c1da..81fc8b257 100644 --- a/src/Magnum/Test/MultisampleTextureGLTest.cpp +++ b/src/Magnum/Test/MultisampleTextureGLTest.cpp @@ -40,6 +40,11 @@ struct MultisampleTextureGLTest: AbstractOpenGLTester { void construct2DArray(); #endif + void wrap2D(); + #ifndef MAGNUM_TARGET_GLES + void wrap2DArray(); + #endif + void bind2D(); #ifndef MAGNUM_TARGET_GLES void bind2DArray(); @@ -67,6 +72,11 @@ MultisampleTextureGLTest::MultisampleTextureGLTest() { &MultisampleTextureGLTest::construct2DArray, #endif + &MultisampleTextureGLTest::wrap2D, + #ifndef MAGNUM_TARGET_GLES + &MultisampleTextureGLTest::wrap2DArray, + #endif + &MultisampleTextureGLTest::bind2D, #ifndef MAGNUM_TARGET_GLES &MultisampleTextureGLTest::bind2DArray, @@ -124,6 +134,49 @@ void MultisampleTextureGLTest::construct2DArray() { } #endif +void MultisampleTextureGLTest::wrap2D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string{" is not supported."}); + #else + if(!Context::current()->isVersionSupported(Version::GLES310)) + CORRADE_SKIP("OpenGL ES 3.1 is not supported."); + #endif + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = MultisampleTexture2D::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + MultisampleTexture2D::wrap(id); + glDeleteTextures(1, &id); +} + +#ifndef MAGNUM_TARGET_GLES +void MultisampleTextureGLTest::wrap2DArray() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string{" is not supported."}); + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = MultisampleTexture2DArray::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + MultisampleTexture2DArray::wrap(id); + glDeleteTextures(1, &id); +} +#endif + void MultisampleTextureGLTest::bind2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Test/PrimitiveQueryGLTest.cpp b/src/Magnum/Test/PrimitiveQueryGLTest.cpp index 7b3f45d7e..d1abbc657 100644 --- a/src/Magnum/Test/PrimitiveQueryGLTest.cpp +++ b/src/Magnum/Test/PrimitiveQueryGLTest.cpp @@ -40,6 +40,8 @@ namespace Magnum { namespace Test { struct PrimitiveQueryGLTest: AbstractOpenGLTester { explicit PrimitiveQueryGLTest(); + void wrap(); + #ifndef MAGNUM_TARGET_GLES void primitivesGenerated(); #endif @@ -47,13 +49,34 @@ struct PrimitiveQueryGLTest: AbstractOpenGLTester { }; PrimitiveQueryGLTest::PrimitiveQueryGLTest() { - addTests({ + addTests({&PrimitiveQueryGLTest::wrap, + #ifndef MAGNUM_TARGET_GLES &PrimitiveQueryGLTest::primitivesGenerated, #endif &PrimitiveQueryGLTest::transformFeedbackPrimitivesWritten}); } +void PrimitiveQueryGLTest::wrap() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::transform_feedback2::string() + std::string(" is not available.")); + #endif + + GLuint id; + glGenQueries(1, &id); + + /* Releasing won't delete anything */ + { + auto query = PrimitiveQuery::wrap(id, PrimitiveQuery::Target::TransformFeedbackPrimitivesWritten, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(query.release(), id); + } + + /* ...so we can wrap it again */ + PrimitiveQuery::wrap(id, PrimitiveQuery::Target::TransformFeedbackPrimitivesWritten); + glDeleteQueries(1, &id); +} + #ifndef MAGNUM_TARGET_GLES void PrimitiveQueryGLTest::primitivesGenerated() { if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Test/RectangleTextureGLTest.cpp b/src/Magnum/Test/RectangleTextureGLTest.cpp index 4c8bc74b9..7a02b2014 100644 --- a/src/Magnum/Test/RectangleTextureGLTest.cpp +++ b/src/Magnum/Test/RectangleTextureGLTest.cpp @@ -41,6 +41,8 @@ struct RectangleTextureGLTest: AbstractOpenGLTester { explicit RectangleTextureGLTest(); void construct(); + void wrap(); + void bind(); void sampling(); @@ -64,6 +66,8 @@ struct RectangleTextureGLTest: AbstractOpenGLTester { RectangleTextureGLTest::RectangleTextureGLTest() { addTests({&RectangleTextureGLTest::construct, + &RectangleTextureGLTest::wrap, + &RectangleTextureGLTest::bind, &RectangleTextureGLTest::sampling, @@ -100,6 +104,24 @@ void RectangleTextureGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void RectangleTextureGLTest::wrap() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = RectangleTexture::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + RectangleTexture::wrap(id); + glDeleteTextures(1, &id); +} + void RectangleTextureGLTest::bind() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); diff --git a/src/Magnum/Test/RenderbufferGLTest.cpp b/src/Magnum/Test/RenderbufferGLTest.cpp index 7eaad6575..13ab49e0c 100644 --- a/src/Magnum/Test/RenderbufferGLTest.cpp +++ b/src/Magnum/Test/RenderbufferGLTest.cpp @@ -38,6 +38,7 @@ struct RenderbufferGLTest: AbstractOpenGLTester { void construct(); void constructCopy(); void constructMove(); + void wrap(); void label(); @@ -49,6 +50,7 @@ RenderbufferGLTest::RenderbufferGLTest() { addTests({&RenderbufferGLTest::construct, &RenderbufferGLTest::constructCopy, &RenderbufferGLTest::constructMove, + &RenderbufferGLTest::wrap, &RenderbufferGLTest::label, @@ -104,6 +106,26 @@ void RenderbufferGLTest::constructMove() { CORRADE_COMPARE(c.id(), id); } +void RenderbufferGLTest::wrap() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::framebuffer_object::string() + std::string(" is not available.")); + #endif + + GLuint id; + glGenRenderbuffers(1, &id); + + /* Releasing won't delete anything */ + { + auto renderbuffer = Renderbuffer::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(renderbuffer.release(), id); + } + + /* ...so we can wrap it again */ + Renderbuffer::wrap(id); + glDeleteRenderbuffers(1, &id); +} + void RenderbufferGLTest::label() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Test/SampleQueryGLTest.cpp b/src/Magnum/Test/SampleQueryGLTest.cpp index 3a1e153f5..0e9065de6 100644 --- a/src/Magnum/Test/SampleQueryGLTest.cpp +++ b/src/Magnum/Test/SampleQueryGLTest.cpp @@ -41,6 +41,8 @@ namespace Magnum { namespace Test { struct SampleQueryGLTest: AbstractOpenGLTester { explicit SampleQueryGLTest(); + void wrap(); + void querySamplesPassed(); #ifndef MAGNUM_TARGET_GLES void conditionalRender(); @@ -48,12 +50,33 @@ struct SampleQueryGLTest: AbstractOpenGLTester { }; SampleQueryGLTest::SampleQueryGLTest() { - addTests({ - &SampleQueryGLTest::querySamplesPassed, - #ifndef MAGNUM_TARGET_GLES - &SampleQueryGLTest::conditionalRender - #endif - }); + addTests({&SampleQueryGLTest::wrap, + + &SampleQueryGLTest::querySamplesPassed, + #ifndef MAGNUM_TARGET_GLES + &SampleQueryGLTest::conditionalRender + #endif + }); +} + +void SampleQueryGLTest::wrap() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::occlusion_query_boolean::string() + std::string(" is not available.")); + #endif + + GLuint id; + glGenQueries(1, &id); + + /* Releasing won't delete anything */ + { + auto query = SampleQuery::wrap(id, SampleQuery::Target::AnySamplesPassed, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(query.release(), id); + } + + /* ...so we can wrap it again */ + SampleQuery::wrap(id, SampleQuery::Target::AnySamplesPassed); + glDeleteQueries(1, &id); } namespace { diff --git a/src/Magnum/Test/TextureArrayGLTest.cpp b/src/Magnum/Test/TextureArrayGLTest.cpp index 0edc55ed9..8c5979fd5 100644 --- a/src/Magnum/Test/TextureArrayGLTest.cpp +++ b/src/Magnum/Test/TextureArrayGLTest.cpp @@ -45,6 +45,11 @@ struct TextureArrayGLTest: AbstractOpenGLTester { #endif void construct2D(); + #ifndef MAGNUM_TARGET_GLES + void wrap1D(); + #endif + void wrap2D(); + #ifndef MAGNUM_TARGET_GLES void bind1D(); #endif @@ -132,6 +137,11 @@ TextureArrayGLTest::TextureArrayGLTest() { #endif &TextureArrayGLTest::construct2D, + #ifndef MAGNUM_TARGET_GLES + &TextureArrayGLTest::wrap1D, + #endif + &TextureArrayGLTest::wrap2D, + #ifndef MAGNUM_TARGET_GLES &TextureArrayGLTest::bind1D, #endif @@ -239,6 +249,43 @@ void TextureArrayGLTest::construct2D() { MAGNUM_VERIFY_NO_ERROR(); } +#ifndef MAGNUM_TARGET_GLES +void TextureArrayGLTest::wrap1D() { + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = Texture1DArray::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + Texture1DArray::wrap(id); + glDeleteTextures(1, &id); +} +#endif + +void TextureArrayGLTest::wrap2D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); + #endif + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = Texture2DArray::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + Texture2DArray::wrap(id); + glDeleteTextures(1, &id); +} + #ifndef MAGNUM_TARGET_GLES void TextureArrayGLTest::bind1D() { if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Test/TextureGLTest.cpp b/src/Magnum/Test/TextureGLTest.cpp index de129e4de..1a9b56d61 100644 --- a/src/Magnum/Test/TextureGLTest.cpp +++ b/src/Magnum/Test/TextureGLTest.cpp @@ -48,6 +48,12 @@ struct TextureGLTest: AbstractOpenGLTester { void construct2D(); void construct3D(); + #ifndef MAGNUM_TARGET_GLES + void wrap1D(); + #endif + void wrap2D(); + void wrap3D(); + #ifndef MAGNUM_TARGET_GLES void bind1D(); #endif @@ -163,6 +169,12 @@ TextureGLTest::TextureGLTest() { &TextureGLTest::construct2D, &TextureGLTest::construct3D, + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::wrap1D, + #endif + &TextureGLTest::wrap2D, + &TextureGLTest::wrap3D, + #ifndef MAGNUM_TARGET_GLES &TextureGLTest::bind1D, #endif @@ -309,6 +321,58 @@ void TextureGLTest::construct3D() { MAGNUM_VERIFY_NO_ERROR(); } +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::wrap1D() { + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = Texture1D::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + Texture1D::wrap(id); + glDeleteTextures(1, &id); +} +#endif + +void TextureGLTest::wrap2D() { + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = Texture2D::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + Texture2D::wrap(id); + glDeleteTextures(1, &id); +} + +void TextureGLTest::wrap3D() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::OES::texture_3D::string() + std::string(" is not supported.")); + #endif + + GLuint id; + glGenTextures(1, &id); + + /* Releasing won't delete anything */ + { + auto texture = Texture3D::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(texture.release(), id); + } + + /* ...so we can wrap it again */ + Texture3D::wrap(id); + glDeleteTextures(1, &id); +} + #ifndef MAGNUM_TARGET_GLES void TextureGLTest::bind1D() { Texture1D texture; diff --git a/src/Magnum/Test/TimeQueryGLTest.cpp b/src/Magnum/Test/TimeQueryGLTest.cpp index fddd09a64..3948aaff7 100644 --- a/src/Magnum/Test/TimeQueryGLTest.cpp +++ b/src/Magnum/Test/TimeQueryGLTest.cpp @@ -31,15 +31,42 @@ namespace Magnum { namespace Test { struct TimeQueryGLTest: AbstractOpenGLTester { explicit TimeQueryGLTest(); + void wrap(); + void queryTime(); void queryTimestamp(); }; TimeQueryGLTest::TimeQueryGLTest() { - addTests({&TimeQueryGLTest::queryTime, + addTests({&TimeQueryGLTest::wrap, + + &TimeQueryGLTest::queryTime, &TimeQueryGLTest::queryTimestamp}); } +void TimeQueryGLTest::wrap() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::timer_query::string() + std::string(" is not available")); + #else + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::disjoint_timer_query::string() + std::string(" is not available")); + #endif + + GLuint id; + glGenQueries(1, &id); + + /* Releasing won't delete anything */ + { + auto query = TimeQuery::wrap(id, TimeQuery::Target::TimeElapsed, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(query.release(), id); + } + + /* ...so we can wrap it again */ + TimeQuery::wrap(id, TimeQuery::Target::TimeElapsed); + glDeleteQueries(1, &id); +} + void TimeQueryGLTest::queryTime() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Test/TransformFeedbackGLTest.cpp b/src/Magnum/Test/TransformFeedbackGLTest.cpp index d52096553..c99dfb9fd 100644 --- a/src/Magnum/Test/TransformFeedbackGLTest.cpp +++ b/src/Magnum/Test/TransformFeedbackGLTest.cpp @@ -39,6 +39,7 @@ struct TransformFeedbackGLTest: AbstractOpenGLTester { void construct(); void constructCopy(); void constructMove(); + void wrap(); void label(); @@ -54,6 +55,7 @@ TransformFeedbackGLTest::TransformFeedbackGLTest() { addTests({&TransformFeedbackGLTest::construct, &TransformFeedbackGLTest::constructCopy, &TransformFeedbackGLTest::constructMove, + &TransformFeedbackGLTest::wrap, &TransformFeedbackGLTest::label, @@ -103,6 +105,21 @@ void TransformFeedbackGLTest::constructMove() { CORRADE_COMPARE(c.id(), id); } +void TransformFeedbackGLTest::wrap() { + GLuint id; + glGenTransformFeedbacks(1, &id); + + /* Releasing won't delete anything */ + { + auto feedback = TransformFeedback::wrap(id, ObjectFlag::DeleteOnDestruction); + CORRADE_COMPARE(feedback.release(), id); + } + + /* ...so we can wrap it again */ + TransformFeedback::wrap(id); + glDeleteTransformFeedbacks(1, &id); +} + void TransformFeedbackGLTest::label() { /* No-Op version is tested in AbstractObjectGLTest */ if(!Context::current()->isExtensionSupported() && diff --git a/src/Magnum/Texture.h b/src/Magnum/Texture.h index c8d164afc..5ba84f746 100644 --- a/src/Magnum/Texture.h +++ b/src/Magnum/Texture.h @@ -159,13 +159,29 @@ template class Texture: public AbstractTexture { return Implementation::maxTextureSize(); } + /** + * @brief Wrap existing OpenGL texture object + * @param id OpenGL texture ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_1D}, @def_gl{TEXTURE_2D} or + * @def_gl{TEXTURE_3D} based on dimension count. Unlike texture created + * using constructor, the OpenGL object is by default not deleted on + * destruction, use @p flags for different behavior. + * @see @ref release() + */ + static Texture wrap(GLuint id, ObjectFlags flags = {}) { + return Texture{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_1D}, + * @see @ref wrap(), @fn_gl{CreateTextures} with @def_gl{TEXTURE_1D}, * @def_gl{TEXTURE_2D} or @def_gl{TEXTURE_3D}, eventually * @fn_gl{GenTextures} */ @@ -923,6 +939,9 @@ template class Texture: public AbstractTexture { return *this; } #endif + + private: + explicit Texture(GLuint id, ObjectFlags flags) noexcept: AbstractTexture{id, Implementation::textureTarget(), flags} {} }; #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/TextureArray.h b/src/Magnum/TextureArray.h index ae31bf074..23bed1ede 100644 --- a/src/Magnum/TextureArray.h +++ b/src/Magnum/TextureArray.h @@ -109,14 +109,31 @@ template class TextureArray: public AbstractTexture { */ static VectorTypeFor maxSize(); + /** + * @brief Wrap existing OpenGL texture array object + * @param id OpenGL texture array ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL texture object + * with target @def_gl{TEXTURE_1D_ARRAY} or @def_gl{TEXTURE_2D_ARRAY} + * based on dimension count. Unlike texture created using constructor, + * the OpenGL object is by default not deleted on destruction, use + * @p flags for different behavior. + * @see @ref release() + */ + static TextureArray wrap(GLuint id, ObjectFlags flags = {}) { + return TextureArray{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL texture object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the texture is created on * first use. - * @see @fn_gl{CreateTextures} with @def_gl{TEXTURE_1D_ARRAY} or - * @def_gl{TEXTURE_2D_ARRAY}, eventually @fn_gl{GenTextures} + * @see @ref wrap(), @fn_gl{CreateTextures} with + * @def_gl{TEXTURE_1D_ARRAY} or @def_gl{TEXTURE_2D_ARRAY}, + * eventually @fn_gl{GenTextures} */ explicit TextureArray(): AbstractTexture(Implementation::textureArrayTarget()) {} @@ -556,6 +573,9 @@ template class TextureArray: public AbstractTexture { return *this; } #endif + + private: + explicit TextureArray(GLuint id, ObjectFlags flags): AbstractTexture{id, Implementation::textureArrayTarget(), flags} {} }; #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/TimeQuery.h b/src/Magnum/TimeQuery.h index 1315ea14c..25ab2cd3f 100644 --- a/src/Magnum/TimeQuery.h +++ b/src/Magnum/TimeQuery.h @@ -104,13 +104,29 @@ class TimeQuery: public AbstractQuery { CORRADE_DEPRECATED("use TimeQuery(Target) instead") explicit TimeQuery() {} #endif + /** + * @brief Wrap existing OpenGL time query object + * @param id OpenGL time query ID + * @param target Query target + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL query object. + * Unlike query created using constructor, the OpenGL object is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static TimeQuery wrap(GLuint id, Target target, ObjectFlags flags = {}) { + return TimeQuery{id, target, flags}; + } + /** * @brief Constructor * * Creates new OpenGL query object. If @extension{ARB,direct_state_access} * (part of OpenGL 4.5) is not available, the query is created on first * use. - * @see @fn_gl{CreateQueries}, eventually @fn_gl{GenQueries} + * @see @ref wrap(), @fn_gl{CreateQueries}, eventually @fn_gl{GenQueries} */ explicit TimeQuery(Target target): AbstractQuery(GLenum(target)) {} @@ -152,6 +168,9 @@ class TimeQuery: public AbstractQuery { return *this; } #endif + + private: + explicit TimeQuery(GLuint id, Target target, ObjectFlags flags) noexcept: AbstractQuery{id, GLenum(target), flags} {} }; } diff --git a/src/Magnum/TransformFeedback.cpp b/src/Magnum/TransformFeedback.cpp index 1a61a5037..ef04d55f1 100644 --- a/src/Magnum/TransformFeedback.cpp +++ b/src/Magnum/TransformFeedback.cpp @@ -96,25 +96,25 @@ Int TransformFeedback::maxBuffers() { } #endif -TransformFeedback::TransformFeedback() { +TransformFeedback::TransformFeedback(): _flags{ObjectFlag::DeleteOnDestruction} { (this->*Context::current()->state().transformFeedback->createImplementation)(); CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding); } void TransformFeedback::createImplementationDefault() { glGenTransformFeedbacks(1, &_id); - _created = false; } #ifndef MAGNUM_TARGET_GLES void TransformFeedback::createImplementationDSA() { glCreateTransformFeedbacks(1, &_id); - _created = true; + _flags |= ObjectFlag::Created; } #endif TransformFeedback::~TransformFeedback() { - if(!_id) return; + /* Moved out or not deleting on destruction, nothing to do */ + if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; /* If bound, remove itself from state */ GLuint& binding = Context::current()->state().transformFeedback->binding; @@ -131,19 +131,19 @@ void TransformFeedback::bindInternal() { /* Bind the transform feedback otherwise, which will also finally create it */ bound = _id; - _created = true; + _flags |= ObjectFlag::Created; glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, _id); } inline void TransformFeedback::createIfNotAlready() { - if(_created) return; + if(_flags & ObjectFlag::Created) return; /* glGen*() does not create the object, just reserves the name. Some commands (such as glObjectLabel()) operate with IDs directly and they require the object to be created. Binding the transform feedback finally creates it. Also all EXT DSA functions implicitly create it. */ bindInternal(); - CORRADE_INTERNAL_ASSERT(_created); + CORRADE_INTERNAL_ASSERT(_flags & ObjectFlag::Created); } #ifndef MAGNUM_TARGET_WEBGL diff --git a/src/Magnum/TransformFeedback.h b/src/Magnum/TransformFeedback.h index 9ac7afd96..5605948b4 100644 --- a/src/Magnum/TransformFeedback.h +++ b/src/Magnum/TransformFeedback.h @@ -147,13 +147,28 @@ class MAGNUM_EXPORT TransformFeedback: public AbstractObject { static Int maxBuffers(); #endif + /** + * @brief Wrap existing OpenGL transform feedback object + * @param id OpenGL transform feedback ID + * @param flags Object creation flags + * + * The @p id is expected to be of an existing OpenGL transform feedback + * object. Unlike renderbuffer created using constructor, the OpenGL + * object is by default not deleted on destruction, use @p flags for + * different behavior. + * @see @ref release() + */ + static TransformFeedback wrap(GLuint id, ObjectFlags flags = {}) { + return TransformFeedback{id, flags}; + } + /** * @brief Constructor * * Creates new OpenGL transform feedback object. If * @extension{ARB,direct_state_access} (part of OpenGL 4.5) is not * available, the transform feedback object is created on first use. - * @see @fn_gl{CreateTransformFeedbacks}, eventually + * @see @ref wrap(), @fn_gl{CreateTransformFeedbacks}, eventually * @fn_gl{GenTransformFeedbacks} */ explicit TransformFeedback(); @@ -168,7 +183,7 @@ class MAGNUM_EXPORT TransformFeedback: public AbstractObject { * @brief Destructor * * Deletes associated OpenGL transform feedback object. - * @see @fn_gl{DeleteTransformFeedbacks} + * @see @ref wrap(), @ref release(), @fn_gl{DeleteTransformFeedbacks} */ ~TransformFeedback(); @@ -181,6 +196,16 @@ class MAGNUM_EXPORT TransformFeedback: public AbstractObject { /** @brief OpenGL transform feedback ID */ GLuint id() const { return _id; } + /** + * @brief Release OpenGL object + * + * Releases ownership of OpenGL transform feedback object and returns + * its ID so it is not deleted on destruction. The internal state is + * then equivalent to moved-from state. + * @see @ref wrap() + */ + GLuint release(); + #ifndef MAGNUM_TARGET_WEBGL /** * @brief Transform feedback label @@ -347,6 +372,8 @@ class MAGNUM_EXPORT TransformFeedback: public AbstractObject { void end(); private: + explicit TransformFeedback(GLuint id, ObjectFlags flags) noexcept: _id{id}, _flags{flags} {} + void bindInternal(); void MAGNUM_LOCAL createIfNotAlready(); @@ -375,20 +402,26 @@ class MAGNUM_EXPORT TransformFeedback: public AbstractObject { #endif GLuint _id; - bool _created; /* see createIfNotAlready() for details */ + ObjectFlags _flags; }; -inline TransformFeedback::TransformFeedback(TransformFeedback&& other) noexcept: _id{other._id}, _created{other._created} { +inline TransformFeedback::TransformFeedback(TransformFeedback&& other) noexcept: _id{other._id}, _flags{other._flags} { other._id = 0; } inline TransformFeedback& TransformFeedback::operator=(TransformFeedback&& other) noexcept { using std::swap; swap(_id, other._id); - swap(_created, other._created); + swap(_flags, other._flags); return *this; } +inline GLuint TransformFeedback::release() { + const GLuint id = _id; + _id = 0; + return id; +} + } #else #error this header is not available in OpenGL ES 2.0 build