diff --git a/doc/changelog.dox b/doc/changelog.dox index a787486df..122a7795a 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -62,6 +62,10 @@ See also: - To prevent nothing being rendered by accident, @ref GL::Mesh::setCount() and @ref GL::MeshView::setCount() now has always to be called, even just to set the count to @cpp 0 @ce. +- @ref GL::Mesh::addVertexBuffer(), @ref GL::Mesh::addVertexBufferInstanced() + @ref GL::Mesh::setIndexBuffer() can now take ownership of a passed + @ref GL::Buffer to simplify resource management on user side. See + @ref GL-Mesh-buffer-ownership for more information. @subsubsection changelog-latest-changes-platform Platform libraries diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index 85e11fa1d..b83a437e9 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -1075,6 +1075,23 @@ mesh.addVertexBuffer(colorBuffer, 0, 4, GL::DynamicAttribute{ /* [Mesh-dynamic] */ } +{ +GL::Mesh mesh; +/* [Mesh-buffer-ownership] */ +GL::Buffer vertices, indices; +// ... +mesh.addVertexBuffer(std::move(vertices), 0, + Shaders::Phong::Position{}, + Shaders::Phong::Normal{}) + .setIndexBuffer(std::move(indices), 0, MeshIndexType::UnsignedInt); +/* [Mesh-buffer-ownership] */ + +/* [Mesh-buffer-ownership-multiple] */ +mesh.addVertexBuffer(vertices, 0, Shaders::Phong::Position{}, 20) + .addVertexBuffer(std::move(vertices), 0, 20, Shaders::Phong::Normal{}); +/* [Mesh-buffer-ownership-multiple] */ +} + { /* [Mesh-addVertexBuffer1] */ GL::Buffer buffer; diff --git a/src/Magnum/GL/Implementation/MeshState.cpp b/src/Magnum/GL/Implementation/MeshState.cpp index 42786a912..9bef6fa6b 100644 --- a/src/Magnum/GL/Implementation/MeshState.cpp +++ b/src/Magnum/GL/Implementation/MeshState.cpp @@ -69,6 +69,7 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector(start); static_cast(end); #endif - (this->*Context::current().state().mesh->bindIndexBufferImplementation)(buffer); + (this->*Context::current().state().mesh->bindIndexBufferImplementation)(_indexBuffer); + return *this; +} + +Mesh& Mesh::setIndexBuffer(Buffer& buffer, const GLintptr offset, const MeshIndexType type, const UnsignedInt start, const UnsignedInt end) { + setIndexBuffer(Buffer::wrap(buffer.id()), offset, type, start, end); return *this; } @@ -545,12 +552,20 @@ void Mesh::createImplementationVAO() { glGenVertexArraysOES(1, &_id); #endif CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding); + + static_assert(sizeof(_attributes) >= sizeof(std::vector), + "attribute storage buffer size too small"); + new(&_attributes) std::vector; } #ifndef MAGNUM_TARGET_GLES void Mesh::createImplementationVAODSA() { glCreateVertexArrays(1, &_id); _flags |= ObjectFlag::Created; + + static_assert(sizeof(_attributes) >= sizeof(std::vector), + "attribute storage buffer size too small"); + new(&_attributes) std::vector; } #endif @@ -558,14 +573,19 @@ void Mesh::moveConstructImplementationDefault(Mesh&& other) { new(&_attributes) std::vector{std::move(*reinterpret_cast*>(&other._attributes))}; } -void Mesh::moveConstructImplementationVAO(Mesh&&) {} +void Mesh::moveConstructImplementationVAO(Mesh&& other) { + new(&_attributes) std::vector{std::move(*reinterpret_cast*>(&other._attributes))}; +} void Mesh::moveAssignImplementationDefault(Mesh&& other) { std::swap(*reinterpret_cast*>(&_attributes), *reinterpret_cast*>(&other._attributes)); } -void Mesh::moveAssignImplementationVAO(Mesh&&) {} +void Mesh::moveAssignImplementationVAO(Mesh&& other) { + std::swap(*reinterpret_cast*>(&_attributes), + *reinterpret_cast*>(&other._attributes)); +} void Mesh::destroyImplementationDefault() { reinterpret_cast*>(&_attributes)->~vector(); @@ -577,29 +597,30 @@ void Mesh::destroyImplementationVAO() { #else glDeleteVertexArraysOES(1, &_id); #endif + + reinterpret_cast*>(&_attributes)->~vector(); } void Mesh::attributePointerInternal(const Buffer& buffer, const GLuint location, const GLint size, const GLenum type, const DynamicAttribute::Kind kind, const GLintptr offset, const GLsizei stride, const GLuint divisor) { - AttributeLayout l{buffer, location, size, type, kind, offset, stride, divisor}; - attributePointerInternal(l); + attributePointerInternal(AttributeLayout{buffer, location, size, type, kind, offset, stride, divisor}); } -void Mesh::attributePointerInternal(AttributeLayout& attribute) { +void Mesh::attributePointerInternal(AttributeLayout&& attribute) { CORRADE_ASSERT(attribute.buffer.id(), "GL::Mesh::addVertexBuffer(): empty or moved-out Buffer instance was passed", ); - (this->*Context::current().state().mesh->attributePointerImplementation)(attribute); + (this->*Context::current().state().mesh->attributePointerImplementation)(std::move(attribute)); } -void Mesh::attributePointerImplementationDefault(AttributeLayout& attribute) { +void Mesh::attributePointerImplementationDefault(AttributeLayout&& attribute) { #ifdef MAGNUM_TARGET_WEBGL CORRADE_ASSERT(attribute.buffer.targetHint() == Buffer::TargetHint::Array, "GL::Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::TargetHint::Array << "but got" << attribute.buffer.targetHint(), ); #endif - reinterpret_cast*>(&_attributes)->push_back(attribute); + reinterpret_cast*>(&_attributes)->push_back(std::move(attribute)); } -void Mesh::attributePointerImplementationVAO(AttributeLayout& attribute) { +void Mesh::attributePointerImplementationVAO(AttributeLayout&& attribute) { #ifdef MAGNUM_TARGET_WEBGL CORRADE_ASSERT(attribute.buffer.targetHint() == Buffer::TargetHint::Array, "GL::Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::TargetHint::Array << "but got" << attribute.buffer.targetHint(), ); @@ -610,7 +631,7 @@ void Mesh::attributePointerImplementationVAO(AttributeLayout& attribute) { } #ifndef MAGNUM_TARGET_GLES -void Mesh::attributePointerImplementationDSAEXT(AttributeLayout& attribute) { +void Mesh::attributePointerImplementationDSAEXT(AttributeLayout&& attribute) { _flags |= ObjectFlag::Created; glEnableVertexArrayAttribEXT(_id, attribute.location); @@ -680,6 +701,25 @@ void Mesh::vertexAttribDivisorImplementationNV(const GLuint index, const GLuint #endif #endif +void Mesh::acquireVertexBuffer(Buffer&& buffer) { + (this->*Context::current().state().mesh->acquireVertexBufferImplementation)(std::move(buffer)); +} + +void Mesh::acquireVertexBufferImplementationDefault(Buffer&& buffer) { + /* The last added buffer should be this one, replace it with a owning one */ + auto& attributes = *reinterpret_cast*>(&_attributes); + CORRADE_INTERNAL_ASSERT(!attributes.empty() && attributes.back().buffer.id() == buffer.id() && buffer.id()); + attributes.back().buffer.release(); /* so we swap back a zero ID */ + attributes.back().buffer = std::move(buffer); +} + +void Mesh::acquireVertexBufferImplementationVAO(Buffer&& buffer) { + CORRADE_INTERNAL_ASSERT(buffer.id()); + /* With VAOs we are not maintaining the attribute list, so just store the + buffer directly */ + reinterpret_cast*>(&_attributes)->emplace_back(std::move(buffer)); +} + void Mesh::bindIndexBufferImplementationDefault(Buffer&) {} void Mesh::bindIndexBufferImplementationVAO(Buffer& buffer) { diff --git a/src/Magnum/GL/Mesh.h b/src/Magnum/GL/Mesh.h index 4a432dc2f..811c1d8e6 100644 --- a/src/Magnum/GL/Mesh.h +++ b/src/Magnum/GL/Mesh.h @@ -192,11 +192,11 @@ There is also @ref MeshTools::compile() function which operates directly on @ref Trade::MeshData2D / @ref Trade::MeshData3D and returns fully configured mesh and vertex/index buffers for use with stock shaders. -Note that neither vertex buffers nor index buffer is managed (e.g. deleted on -destruction) by the mesh, so you have to manage them on your own and ensure -that they are available for whole mesh lifetime. On the other hand it allows -you to use one buffer for more meshes (each mesh for example configured for -different usage) or store data for more meshes in one buffer. +@attention Note that, by default, neither vertex buffers nor index buffer is + managed (e.g. deleted on destruction) by the mesh, so you have to manage + them on your own and ensure that they are available for whole mesh + lifetime. See @ref GL-Mesh-buffer-ownership for a way to transfer buffer + ownership to the mesh. If vertex/index count or instance count is zero, the mesh is empty and no draw commands are issued when calling @ref draw(). @@ -238,6 +238,24 @@ this: @snippet MagnumGL.cpp Mesh-dynamic +@section GL-Mesh-buffer-ownership Transfering buffer ownership + +If a vertex/index buffer is used only by a single mesh, it's possible to +transfer its ownership to the mesh itself to simplify resource management on +the user side. Simply use the @ref addVertexBuffer() / +@ref addVertexBufferInstanced() and @ref setIndexBuffer() overloads that take +a @ref Buffer as a rvalue: + +@snippet MagnumGL.cpp Mesh-buffer-ownership + +While this allows you to destruct the buffer instances and pass just the mesh +around, this also means you lose a way to access or update the buffers. If +adding the same buffer multiple times or using it for both vertex and index +data, be sure to transfer the ownership last to avoid the other functions +getting only a moved-out instance. For example: + +@snippet MagnumGL.cpp Mesh-buffer-ownership-multiple + @section GL-Mesh-rendering Rendering meshes Basic workflow is: bind specific framebuffer for drawing (if needed), set up @@ -727,7 +745,7 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { } /** - * @brief Add buffer with dynamic vertex attributes for use with given shader + * @brief Add vertex buffer with dynamic vertex attributes * @return Reference to self (for method chaining) * * Equivalent to @ref addVertexBuffer(Buffer&, GLintptr, const T&... attributes) @@ -741,7 +759,7 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { } /** - * @brief Add buffer with dynamic vertex attributes for use with given shader + * @brief Add instanced vertex buffer with dynamic vertex attributes * @return Reference to self (for method chaining) * * Equivalent to @ref addVertexBufferInstanced(Buffer&, UnsignedInt, GLintptr, const T&... attributes) @@ -752,6 +770,62 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { */ Mesh& addVertexBufferInstanced(Buffer& buffer, UnsignedInt divisor, GLintptr offset, GLsizei stride, const DynamicAttribute& attribute); + /** + * @brief Add vertex buffer with ownership transfer + * @return Reference to self (for method chaining) + * + * Unlike @ref addVertexBuffer(Buffer&, GLintptr, const T&... attributes) + * this function takes ownership of @p buffer. See + * @ref GL-Mesh-buffer-ownership for more information. + */ + template inline Mesh& addVertexBuffer(Buffer&& buffer, GLintptr offset, const T&... attributes) { + addVertexBuffer(buffer, offset, attributes...); + acquireVertexBuffer(std::move(buffer)); + return *this; + } + + /** + * @brief Add instanced vertex buffer with ownership transfer + * @return Reference to self (for method chaining) + * + * Unlike @ref addVertexBufferInstanced(Buffer&, UnsignedInt, GLintptr, const T&... attributes) + * this function takes ownership of @p buffer. See + * @ref GL-Mesh-buffer-ownership for more information. + */ + template inline Mesh& addVertexBufferInstanced(Buffer&& buffer, UnsignedInt divisor, GLintptr offset, const T&... attributes) { + addVertexBufferInstanced(buffer, divisor, offset, attributes...); + acquireVertexBuffer(std::move(buffer)); + return *this; + } + + /** + * @brief Add vertex buffer with dynamic vertex attributes with ownership transfer + * @return Reference to self (for method chaining) + * + * Unlike @ref addVertexBuffer(Buffer&, GLintptr, GLsizei, const DynamicAttribute&) + * this function takes ownership of @p buffer. See + * @ref GL-Mesh-buffer-ownership for more information. + */ + Mesh& addVertexBuffer(Buffer&& buffer, GLintptr offset, GLsizei stride, const DynamicAttribute& attribute) { + addVertexBuffer(buffer, offset, stride, attribute); + acquireVertexBuffer(std::move(buffer)); + return *this; + } + + /** + * @brief Add instanced vertex buffer with dynamic vertex attributes with ownership transfer + * @return Reference to self (for method chaining) + * + * Unlike @ref addVertexBufferInstanced(Buffer&, UnsignedInt, GLintptr, GLsizei, const DynamicAttribute&) + * this function takes ownership of @p buffer. See + * @ref GL-Mesh-buffer-ownership for more information. + */ + Mesh& addVertexBufferInstanced(Buffer&& buffer, UnsignedInt divisor, GLintptr offset, GLsizei stride, const DynamicAttribute& attribute) { + addVertexBufferInstanced(buffer, divisor, offset, stride, attribute); + acquireVertexBuffer(std::move(buffer)); + return *this; + } + /** * @brief Set index buffer * @param buffer Index buffer @@ -807,6 +881,36 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { return setIndexBuffer(buffer, offset, meshIndexType(type), 0, 0); } + /** + * @brief Set index buffer with ownership transfer + * + * Unlike @ref setIndexBuffer(Buffer&, GLintptr, MeshIndexType, UnsignedInt, UnsignedInt) + * this function takes ownership of @p buffer. See + * @ref GL-Mesh-buffer-ownership for more information. + */ + Mesh& setIndexBuffer(Buffer&& buffer, GLintptr offset, MeshIndexType type, UnsignedInt start, UnsignedInt end); + + /** @overload */ + Mesh& setIndexBuffer(Buffer&& buffer, GLintptr offset, Magnum::MeshIndexType type, UnsignedInt start, UnsignedInt end) { + return setIndexBuffer(std::move(buffer), offset, meshIndexType(type), start, end); + } + + /** + * @brief Set index buffer with ownership transfer + * + * Unlike @ref setIndexBuffer(Buffer&, GLintptr, MeshIndexType) this + * function takes ownership of @p buffer. See + * @ref GL-Mesh-buffer-ownership for more information. + */ + Mesh& setIndexBuffer(Buffer&& buffer, GLintptr offset, MeshIndexType type) { + return setIndexBuffer(std::move(buffer), offset, type, 0, 0); + } + + /** @overload */ + Mesh& setIndexBuffer(Buffer&& buffer, GLintptr offset, Magnum::MeshIndexType type) { + return setIndexBuffer(std::move(buffer), offset, meshIndexType(type), 0, 0); + } + /** * @brief Draw the mesh * @param shader Shader to use for drawing @@ -995,11 +1099,11 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { void MAGNUM_GL_LOCAL destroyImplementationVAO(); void attributePointerInternal(const Buffer& buffer, GLuint location, GLint size, GLenum type, DynamicAttribute::Kind kind, GLintptr offset, GLsizei stride, GLuint divisor); - void MAGNUM_GL_LOCAL attributePointerInternal(AttributeLayout& attribute); - void MAGNUM_GL_LOCAL attributePointerImplementationDefault(AttributeLayout& attribute); - void MAGNUM_GL_LOCAL attributePointerImplementationVAO(AttributeLayout& attribute); + void MAGNUM_GL_LOCAL attributePointerInternal(AttributeLayout&& attribute); + void MAGNUM_GL_LOCAL attributePointerImplementationDefault(AttributeLayout&& attribute); + void MAGNUM_GL_LOCAL attributePointerImplementationVAO(AttributeLayout&& attribute); #ifndef MAGNUM_TARGET_GLES - void MAGNUM_GL_LOCAL attributePointerImplementationDSAEXT(AttributeLayout& attribute); + void MAGNUM_GL_LOCAL attributePointerImplementationDSAEXT(AttributeLayout&& attribute); #endif void MAGNUM_GL_LOCAL vertexAttribPointer(AttributeLayout& attribute); @@ -1014,6 +1118,10 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { #endif #endif + void acquireVertexBuffer(Buffer&& buffer); + void MAGNUM_GL_LOCAL acquireVertexBufferImplementationDefault(Buffer&& buffer); + void MAGNUM_GL_LOCAL acquireVertexBufferImplementationVAO(Buffer&& buffer); + void MAGNUM_GL_LOCAL bindIndexBufferImplementationDefault(Buffer&); void MAGNUM_GL_LOCAL bindIndexBufferImplementationVAO(Buffer& buffer); @@ -1055,8 +1163,9 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject { MeshIndexType _indexType{}; Buffer _indexBuffer{NoCreate}; - /* Storage for std::vector with attribute layout / attribute buffer - instances. 4 pointers should be one pointer more than enough. */ + /* Storage for either std::vector (in case of no VAOs) + or std::vector (in case of VAOs). 4 pointers should be one + pointer more than enough. */ struct { std::intptr_t data[4]; } _attributes; }; diff --git a/src/Magnum/GL/Test/MeshGLTest.cpp b/src/Magnum/GL/Test/MeshGLTest.cpp index e098a3004..d2f43b319 100644 --- a/src/Magnum/GL/Test/MeshGLTest.cpp +++ b/src/Magnum/GL/Test/MeshGLTest.cpp @@ -118,12 +118,18 @@ struct MeshGLTest: OpenGLTester { void addVertexBufferMultipleGaps(); void addVertexBufferMovedOutInstance(); + void addVertexBufferTransferOwnwership(); + void addVertexBufferInstancedTransferOwnwership(); + void addVertexBufferDynamicTransferOwnwership(); + void addVertexBufferInstancedDynamicTransferOwnwership(); template void setIndexBuffer(); template void setIndexBufferRange(); void setIndexBufferUnsignedInt(); void setIndexBufferMovedOutInstance(); + template void setIndexBufferTransferOwnership(); + template void setIndexBufferRangeTransferOwnership(); void unbindVAOWhenSettingIndexBufferData(); void unbindVAOBeforeEnteringExternalSection(); @@ -227,6 +233,10 @@ MeshGLTest::MeshGLTest() { &MeshGLTest::addVertexBufferMultipleGaps, &MeshGLTest::addVertexBufferMovedOutInstance, + &MeshGLTest::addVertexBufferTransferOwnwership, + &MeshGLTest::addVertexBufferInstancedTransferOwnwership, + &MeshGLTest::addVertexBufferDynamicTransferOwnwership, + &MeshGLTest::addVertexBufferInstancedDynamicTransferOwnwership, &MeshGLTest::setIndexBuffer, &MeshGLTest::setIndexBuffer, @@ -235,6 +245,10 @@ MeshGLTest::MeshGLTest() { &MeshGLTest::setIndexBufferUnsignedInt, &MeshGLTest::setIndexBufferMovedOutInstance, + &MeshGLTest::setIndexBufferTransferOwnership, + &MeshGLTest::setIndexBufferTransferOwnership, + &MeshGLTest::setIndexBufferRangeTransferOwnership, + &MeshGLTest::setIndexBufferRangeTransferOwnership, &MeshGLTest::unbindVAOWhenSettingIndexBufferData, &MeshGLTest::unbindVAOBeforeEnteringExternalSection, @@ -1663,6 +1677,126 @@ void MeshGLTest::addVertexBufferMovedOutInstance() { CORRADE_COMPARE(out.str(), "GL::Mesh::addVertexBuffer(): empty or moved-out Buffer instance was passed\n"); } +void MeshGLTest::addVertexBufferTransferOwnwership() { + const Float data = 1.0f; + Buffer buffer; + buffer.setData({&data, 1}, BufferUsage::StaticDraw); + + const GLuint id = buffer.id(); + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBuffer(buffer, 0, Attribute<0, Float>{}); + CORRADE_VERIFY(buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBuffer(std::move(buffer), 0, Attribute<0, Float>{}); + CORRADE_VERIFY(!buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(!glIsBuffer(id)); +} + +void MeshGLTest::addVertexBufferInstancedTransferOwnwership() { + const Float data = 1.0f; + Buffer buffer; + buffer.setData({&data, 1}, BufferUsage::StaticDraw); + + const GLuint id = buffer.id(); + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBufferInstanced(buffer, 1, 0, Attribute<0, Float>{}); + CORRADE_VERIFY(buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBufferInstanced(std::move(buffer), 1, 0, Attribute<0, Float>{}); + CORRADE_VERIFY(!buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(!glIsBuffer(id)); +} + +void MeshGLTest::addVertexBufferDynamicTransferOwnwership() { + const Float data = 1.0f; + Buffer buffer; + buffer.setData({&data, 1}, BufferUsage::StaticDraw); + + const GLuint id = buffer.id(); + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBuffer(buffer, 0, 4, DynamicAttribute{ + DynamicAttribute::Kind::GenericNormalized, 0, + DynamicAttribute::Components::One, + DynamicAttribute::DataType::Float}); + CORRADE_VERIFY(buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBuffer(std::move(buffer), 0, 4, DynamicAttribute{ + DynamicAttribute::Kind::GenericNormalized, 0, + DynamicAttribute::Components::One, + DynamicAttribute::DataType::Float}); + CORRADE_VERIFY(!buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(!glIsBuffer(id)); +} + +void MeshGLTest::addVertexBufferInstancedDynamicTransferOwnwership() { + const Float data = 1.0f; + Buffer buffer; + buffer.setData({&data, 1}, BufferUsage::StaticDraw); + + const GLuint id = buffer.id(); + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBufferInstanced(buffer, 1, 0, 4, DynamicAttribute{ + DynamicAttribute::Kind::GenericNormalized, 0, + DynamicAttribute::Components::One, + DynamicAttribute::DataType::Float}); + CORRADE_VERIFY(buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.addVertexBufferInstanced(std::move(buffer), 1, 0, 4, DynamicAttribute{ + DynamicAttribute::Kind::GenericNormalized, 0, + DynamicAttribute::Components::One, + DynamicAttribute::DataType::Float}); + CORRADE_VERIFY(!buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(!glIsBuffer(id)); +} + namespace { const Float indexedVertexData[] = { 0.0f, /* Offset */ @@ -1829,6 +1963,70 @@ void MeshGLTest::setIndexBufferMovedOutInstance() { CORRADE_COMPARE(out.str(), "GL::Mesh::setIndexBuffer(): empty or moved-out Buffer instance was passed\n"); } +template void MeshGLTest::setIndexBufferTransferOwnership() { + setTestCaseName(std::is_same::value ? + "setIndexBufferTransferOwnership" : + "setIndexBufferTransferOwnership"); + + const UnsignedShort data = 0; + Buffer buffer; + buffer.setData({&data, 1}, BufferUsage::StaticDraw); + + const GLuint id = buffer.id(); + CORRADE_VERIFY(glIsBuffer(id)); + + { + + Mesh mesh; + mesh.setIndexBuffer(buffer, 0, T::UnsignedShort); + CORRADE_VERIFY(buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(glIsBuffer(id)); + + { + + Mesh mesh; + mesh.setIndexBuffer(std::move(buffer), 0, T::UnsignedShort); + CORRADE_VERIFY(!buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(!glIsBuffer(id)); +} + +template void MeshGLTest::setIndexBufferRangeTransferOwnership() { + setTestCaseName(std::is_same::value ? + "setIndexBufferRangeTransferOwnership" : + "setIndexBufferRangeTransferOwnership"); + + const UnsignedShort data = 0; + Buffer buffer; + buffer.setData({&data, 1}, BufferUsage::StaticDraw); + + const GLuint id = buffer.id(); + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.setIndexBuffer(buffer, 0, T::UnsignedShort, 0, 1); + CORRADE_VERIFY(buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(glIsBuffer(id)); + + { + Mesh mesh; + mesh.setIndexBuffer(std::move(buffer), 0, T::UnsignedShort, 0, 1); + CORRADE_VERIFY(!buffer.id()); + CORRADE_VERIFY(glIsBuffer(id)); + } + + CORRADE_VERIFY(!glIsBuffer(id)); +} + void MeshGLTest::unbindVAOWhenSettingIndexBufferData() { #ifndef MAGNUM_TARGET_GLES if(!Context::current().isExtensionSupported())