Browse Source

GL: make it possible for a Mesh to own the vertex/index buffers.

I wanted to do this since 2015.
pull/255/head
Vladimír Vondruš 8 years ago
parent
commit
9be24a241e
  1. 4
      doc/changelog.dox
  2. 17
      doc/snippets/MagnumGL.cpp
  3. 2
      src/Magnum/GL/Implementation/MeshState.cpp
  4. 3
      src/Magnum/GL/Implementation/MeshState.h
  5. 74
      src/Magnum/GL/Mesh.cpp
  6. 135
      src/Magnum/GL/Mesh.h
  7. 198
      src/Magnum/GL/Test/MeshGLTest.cpp

4
doc/changelog.dox

@ -62,6 +62,10 @@ See also:
- To prevent nothing being rendered by accident, @ref GL::Mesh::setCount() - 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 and @ref GL::MeshView::setCount() now has always to be called, even just to
set the count to @cpp 0 @ce. 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 @subsubsection changelog-latest-changes-platform Platform libraries

17
doc/snippets/MagnumGL.cpp

@ -1075,6 +1075,23 @@ mesh.addVertexBuffer(colorBuffer, 0, 4, GL::DynamicAttribute{
/* [Mesh-dynamic] */ /* [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] */ /* [Mesh-addVertexBuffer1] */
GL::Buffer buffer; GL::Buffer buffer;

2
src/Magnum/GL/Implementation/MeshState.cpp

@ -69,6 +69,7 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector<s
attributePointerImplementation = &Mesh::attributePointerImplementationVAO; attributePointerImplementation = &Mesh::attributePointerImplementationVAO;
} }
acquireVertexBufferImplementation = &Mesh::acquireVertexBufferImplementationVAO;
bindIndexBufferImplementation = &Mesh::bindIndexBufferImplementationVAO; bindIndexBufferImplementation = &Mesh::bindIndexBufferImplementationVAO;
bindVAOImplementation = &Mesh::bindVAOImplementationVAO; bindVAOImplementation = &Mesh::bindVAOImplementationVAO;
bindImplementation = &Mesh::bindImplementationVAO; bindImplementation = &Mesh::bindImplementationVAO;
@ -81,6 +82,7 @@ MeshState::MeshState(Context& context, ContextState& contextState, std::vector<s
moveAssignImplementation = &Mesh::moveAssignImplementationDefault; moveAssignImplementation = &Mesh::moveAssignImplementationDefault;
destroyImplementation = &Mesh::destroyImplementationDefault; destroyImplementation = &Mesh::destroyImplementationDefault;
attributePointerImplementation = &Mesh::attributePointerImplementationDefault; attributePointerImplementation = &Mesh::attributePointerImplementationDefault;
acquireVertexBufferImplementation = &Mesh::acquireVertexBufferImplementationDefault;
bindIndexBufferImplementation = &Mesh::bindIndexBufferImplementationDefault; bindIndexBufferImplementation = &Mesh::bindIndexBufferImplementationDefault;
bindVAOImplementation = &Mesh::bindVAOImplementationDefault; bindVAOImplementation = &Mesh::bindVAOImplementationDefault;
bindImplementation = &Mesh::bindImplementationDefault; bindImplementation = &Mesh::bindImplementationDefault;

3
src/Magnum/GL/Implementation/MeshState.h

@ -44,10 +44,11 @@ struct MeshState {
void(Mesh::*moveConstructImplementation)(Mesh&&); void(Mesh::*moveConstructImplementation)(Mesh&&);
void(Mesh::*moveAssignImplementation)(Mesh&&); void(Mesh::*moveAssignImplementation)(Mesh&&);
void(Mesh::*destroyImplementation)(); void(Mesh::*destroyImplementation)();
void(Mesh::*attributePointerImplementation)(Mesh::AttributeLayout&); void(Mesh::*attributePointerImplementation)(Mesh::AttributeLayout&&);
#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
void(Mesh::*vertexAttribDivisorImplementation)(GLuint, GLuint); void(Mesh::*vertexAttribDivisorImplementation)(GLuint, GLuint);
#endif #endif
void(Mesh::*acquireVertexBufferImplementation)(Buffer&&);
void(Mesh::*bindIndexBufferImplementation)(Buffer&); void(Mesh::*bindIndexBufferImplementation)(Buffer&);
void(Mesh::*bindImplementation)(); void(Mesh::*bindImplementation)();
void(Mesh::*unbindImplementation)(); void(Mesh::*unbindImplementation)();

74
src/Magnum/GL/Mesh.cpp

@ -134,7 +134,10 @@ Debug& operator<<(Debug& debug, MeshIndexType value) {
struct Mesh::AttributeLayout { struct Mesh::AttributeLayout {
explicit AttributeLayout(const Buffer& buffer, GLuint location, GLint size, GLenum type, DynamicAttribute::Kind kind, GLintptr offset, GLsizei stride, GLuint divisor) noexcept: buffer{Buffer::wrap(buffer.id())}, location{location}, size{size}, type{type}, kind{kind}, offset{offset}, stride{stride}, divisor{divisor} {} explicit AttributeLayout(const Buffer& buffer, GLuint location, GLint size, GLenum type, DynamicAttribute::Kind kind, GLintptr offset, GLsizei stride, GLuint divisor) noexcept: buffer{Buffer::wrap(buffer.id())}, location{location}, size{size}, type{type}, kind{kind}, offset{offset}, stride{stride}, divisor{divisor} {}
explicit AttributeLayout(const AttributeLayout& other): buffer{Buffer::wrap(other.buffer.id())}, location{other.location}, size{other.size}, type{other.type}, kind{other.kind}, offset{other.offset}, stride{other.stride}, divisor{other.divisor} {} AttributeLayout(AttributeLayout&&) noexcept = default;
AttributeLayout(const AttributeLayout&) noexcept = delete;
AttributeLayout& operator=(AttributeLayout&&) noexcept = default;
AttributeLayout& operator=(const AttributeLayout&) noexcept = delete;
Buffer buffer; Buffer buffer;
GLuint location; GLuint location;
@ -311,19 +314,18 @@ UnsignedInt Mesh::indexTypeSize() const {
} }
Mesh& Mesh::addVertexBufferInstanced(Buffer& buffer, const UnsignedInt divisor, const GLintptr offset, const GLsizei stride, const DynamicAttribute& attribute) { Mesh& Mesh::addVertexBufferInstanced(Buffer& buffer, const UnsignedInt divisor, const GLintptr offset, const GLsizei stride, const DynamicAttribute& attribute) {
AttributeLayout l{buffer, attributePointerInternal(AttributeLayout{buffer,
attribute.location(), attribute.location(),
GLint(attribute.components()), GLint(attribute.components()),
GLenum(attribute.dataType()), GLenum(attribute.dataType()),
attribute.kind(), attribute.kind(),
offset, offset,
stride, stride,
divisor}; divisor});
attributePointerInternal(l);
return *this; return *this;
} }
Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, MeshIndexType type, UnsignedInt start, UnsignedInt end) { Mesh& Mesh::setIndexBuffer(Buffer&& buffer, GLintptr offset, MeshIndexType type, UnsignedInt start, UnsignedInt end) {
CORRADE_ASSERT(buffer.id(), CORRADE_ASSERT(buffer.id(),
"GL::Mesh::setIndexBuffer(): empty or moved-out Buffer instance was passed", *this); "GL::Mesh::setIndexBuffer(): empty or moved-out Buffer instance was passed", *this);
#ifdef MAGNUM_TARGET_WEBGL #ifdef MAGNUM_TARGET_WEBGL
@ -331,7 +333,7 @@ Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, MeshIndexType type,
"GL::Mesh::setIndexBuffer(): the buffer has unexpected target hint, expected" << Buffer::TargetHint::ElementArray << "but got" << buffer.targetHint(), *this); "GL::Mesh::setIndexBuffer(): the buffer has unexpected target hint, expected" << Buffer::TargetHint::ElementArray << "but got" << buffer.targetHint(), *this);
#endif #endif
_indexBuffer = Buffer::wrap(buffer.id()); _indexBuffer = std::move(buffer);
_indexOffset = offset; _indexOffset = offset;
_indexType = type; _indexType = type;
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
@ -341,7 +343,12 @@ Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, MeshIndexType type,
static_cast<void>(start); static_cast<void>(start);
static_cast<void>(end); static_cast<void>(end);
#endif #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; return *this;
} }
@ -545,12 +552,20 @@ void Mesh::createImplementationVAO() {
glGenVertexArraysOES(1, &_id); glGenVertexArraysOES(1, &_id);
#endif #endif
CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding); CORRADE_INTERNAL_ASSERT(_id != Implementation::State::DisengagedBinding);
static_assert(sizeof(_attributes) >= sizeof(std::vector<Buffer>),
"attribute storage buffer size too small");
new(&_attributes) std::vector<Buffer>;
} }
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
void Mesh::createImplementationVAODSA() { void Mesh::createImplementationVAODSA() {
glCreateVertexArrays(1, &_id); glCreateVertexArrays(1, &_id);
_flags |= ObjectFlag::Created; _flags |= ObjectFlag::Created;
static_assert(sizeof(_attributes) >= sizeof(std::vector<Buffer>),
"attribute storage buffer size too small");
new(&_attributes) std::vector<Buffer>;
} }
#endif #endif
@ -558,14 +573,19 @@ void Mesh::moveConstructImplementationDefault(Mesh&& other) {
new(&_attributes) std::vector<AttributeLayout>{std::move(*reinterpret_cast<std::vector<AttributeLayout>*>(&other._attributes))}; new(&_attributes) std::vector<AttributeLayout>{std::move(*reinterpret_cast<std::vector<AttributeLayout>*>(&other._attributes))};
} }
void Mesh::moveConstructImplementationVAO(Mesh&&) {} void Mesh::moveConstructImplementationVAO(Mesh&& other) {
new(&_attributes) std::vector<Buffer>{std::move(*reinterpret_cast<std::vector<Buffer>*>(&other._attributes))};
}
void Mesh::moveAssignImplementationDefault(Mesh&& other) { void Mesh::moveAssignImplementationDefault(Mesh&& other) {
std::swap(*reinterpret_cast<std::vector<AttributeLayout>*>(&_attributes), std::swap(*reinterpret_cast<std::vector<AttributeLayout>*>(&_attributes),
*reinterpret_cast<std::vector<AttributeLayout>*>(&other._attributes)); *reinterpret_cast<std::vector<AttributeLayout>*>(&other._attributes));
} }
void Mesh::moveAssignImplementationVAO(Mesh&&) {} void Mesh::moveAssignImplementationVAO(Mesh&& other) {
std::swap(*reinterpret_cast<std::vector<Buffer>*>(&_attributes),
*reinterpret_cast<std::vector<Buffer>*>(&other._attributes));
}
void Mesh::destroyImplementationDefault() { void Mesh::destroyImplementationDefault() {
reinterpret_cast<std::vector<AttributeLayout>*>(&_attributes)->~vector(); reinterpret_cast<std::vector<AttributeLayout>*>(&_attributes)->~vector();
@ -577,29 +597,30 @@ void Mesh::destroyImplementationVAO() {
#else #else
glDeleteVertexArraysOES(1, &_id); glDeleteVertexArraysOES(1, &_id);
#endif #endif
reinterpret_cast<std::vector<Buffer>*>(&_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) { 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(AttributeLayout{buffer, location, size, type, kind, offset, stride, divisor});
attributePointerInternal(l);
} }
void Mesh::attributePointerInternal(AttributeLayout& attribute) { void Mesh::attributePointerInternal(AttributeLayout&& attribute) {
CORRADE_ASSERT(attribute.buffer.id(), CORRADE_ASSERT(attribute.buffer.id(),
"GL::Mesh::addVertexBuffer(): empty or moved-out Buffer instance was passed", ); "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 #ifdef MAGNUM_TARGET_WEBGL
CORRADE_ASSERT(attribute.buffer.targetHint() == Buffer::TargetHint::Array, 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(), ); "GL::Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::TargetHint::Array << "but got" << attribute.buffer.targetHint(), );
#endif #endif
reinterpret_cast<std::vector<AttributeLayout>*>(&_attributes)->push_back(attribute); reinterpret_cast<std::vector<AttributeLayout>*>(&_attributes)->push_back(std::move(attribute));
} }
void Mesh::attributePointerImplementationVAO(AttributeLayout& attribute) { void Mesh::attributePointerImplementationVAO(AttributeLayout&& attribute) {
#ifdef MAGNUM_TARGET_WEBGL #ifdef MAGNUM_TARGET_WEBGL
CORRADE_ASSERT(attribute.buffer.targetHint() == Buffer::TargetHint::Array, 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(), ); "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 #ifndef MAGNUM_TARGET_GLES
void Mesh::attributePointerImplementationDSAEXT(AttributeLayout& attribute) { void Mesh::attributePointerImplementationDSAEXT(AttributeLayout&& attribute) {
_flags |= ObjectFlag::Created; _flags |= ObjectFlag::Created;
glEnableVertexArrayAttribEXT(_id, attribute.location); glEnableVertexArrayAttribEXT(_id, attribute.location);
@ -680,6 +701,25 @@ void Mesh::vertexAttribDivisorImplementationNV(const GLuint index, const GLuint
#endif #endif
#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<std::vector<AttributeLayout>*>(&_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<std::vector<Buffer>*>(&_attributes)->emplace_back(std::move(buffer));
}
void Mesh::bindIndexBufferImplementationDefault(Buffer&) {} void Mesh::bindIndexBufferImplementationDefault(Buffer&) {}
void Mesh::bindIndexBufferImplementationVAO(Buffer& buffer) { void Mesh::bindIndexBufferImplementationVAO(Buffer& buffer) {

135
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 @ref Trade::MeshData2D / @ref Trade::MeshData3D and returns fully configured
mesh and vertex/index buffers for use with stock shaders. mesh and vertex/index buffers for use with stock shaders.
Note that neither vertex buffers nor index buffer is managed (e.g. deleted on @attention Note that, by default, neither vertex buffers nor index buffer is
destruction) by the mesh, so you have to manage them on your own and ensure managed (e.g. deleted on destruction) by the mesh, so you have to manage
that they are available for whole mesh lifetime. On the other hand it allows them on your own and ensure that they are available for whole mesh
you to use one buffer for more meshes (each mesh for example configured for lifetime. See @ref GL-Mesh-buffer-ownership for a way to transfer buffer
different usage) or store data for more meshes in one buffer. ownership to the mesh.
If vertex/index count or instance count is zero, the mesh is empty and no draw If vertex/index count or instance count is zero, the mesh is empty and no draw
commands are issued when calling @ref draw(). commands are issued when calling @ref draw().
@ -238,6 +238,24 @@ this:
@snippet MagnumGL.cpp Mesh-dynamic @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 @section GL-Mesh-rendering Rendering meshes
Basic workflow is: bind specific framebuffer for drawing (if needed), set up 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) * @return Reference to self (for method chaining)
* *
* Equivalent to @ref addVertexBuffer(Buffer&, GLintptr, const T&... attributes) * 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) * @return Reference to self (for method chaining)
* *
* Equivalent to @ref addVertexBufferInstanced(Buffer&, UnsignedInt, GLintptr, const T&... attributes) * 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); 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<class ...T> inline Mesh& addVertexBuffer(Buffer&& buffer, GLintptr offset, const T&... attributes) {
addVertexBuffer<T...>(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<class ...T> inline Mesh& addVertexBufferInstanced(Buffer&& buffer, UnsignedInt divisor, GLintptr offset, const T&... attributes) {
addVertexBufferInstanced<T...>(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 * @brief Set index buffer
* @param buffer 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); 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 * @brief Draw the mesh
* @param shader Shader to use for drawing * @param shader Shader to use for drawing
@ -995,11 +1099,11 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
void MAGNUM_GL_LOCAL destroyImplementationVAO(); 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 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 attributePointerInternal(AttributeLayout&& attribute);
void MAGNUM_GL_LOCAL attributePointerImplementationDefault(AttributeLayout& attribute); void MAGNUM_GL_LOCAL attributePointerImplementationDefault(AttributeLayout&& attribute);
void MAGNUM_GL_LOCAL attributePointerImplementationVAO(AttributeLayout& attribute); void MAGNUM_GL_LOCAL attributePointerImplementationVAO(AttributeLayout&& attribute);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
void MAGNUM_GL_LOCAL attributePointerImplementationDSAEXT(AttributeLayout& attribute); void MAGNUM_GL_LOCAL attributePointerImplementationDSAEXT(AttributeLayout&& attribute);
#endif #endif
void MAGNUM_GL_LOCAL vertexAttribPointer(AttributeLayout& attribute); void MAGNUM_GL_LOCAL vertexAttribPointer(AttributeLayout& attribute);
@ -1014,6 +1118,10 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
#endif #endif
#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 bindIndexBufferImplementationDefault(Buffer&);
void MAGNUM_GL_LOCAL bindIndexBufferImplementationVAO(Buffer& buffer); void MAGNUM_GL_LOCAL bindIndexBufferImplementationVAO(Buffer& buffer);
@ -1055,8 +1163,9 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
MeshIndexType _indexType{}; MeshIndexType _indexType{};
Buffer _indexBuffer{NoCreate}; Buffer _indexBuffer{NoCreate};
/* Storage for std::vector with attribute layout / attribute buffer /* Storage for either std::vector<AttributeLayout> (in case of no VAOs)
instances. 4 pointers should be one pointer more than enough. */ or std::vector<Buffer> (in case of VAOs). 4 pointers should be one
pointer more than enough. */
struct { std::intptr_t data[4]; } _attributes; struct { std::intptr_t data[4]; } _attributes;
}; };

198
src/Magnum/GL/Test/MeshGLTest.cpp

@ -118,12 +118,18 @@ struct MeshGLTest: OpenGLTester {
void addVertexBufferMultipleGaps(); void addVertexBufferMultipleGaps();
void addVertexBufferMovedOutInstance(); void addVertexBufferMovedOutInstance();
void addVertexBufferTransferOwnwership();
void addVertexBufferInstancedTransferOwnwership();
void addVertexBufferDynamicTransferOwnwership();
void addVertexBufferInstancedDynamicTransferOwnwership();
template<class T> void setIndexBuffer(); template<class T> void setIndexBuffer();
template<class T> void setIndexBufferRange(); template<class T> void setIndexBufferRange();
void setIndexBufferUnsignedInt(); void setIndexBufferUnsignedInt();
void setIndexBufferMovedOutInstance(); void setIndexBufferMovedOutInstance();
template<class T> void setIndexBufferTransferOwnership();
template<class T> void setIndexBufferRangeTransferOwnership();
void unbindVAOWhenSettingIndexBufferData(); void unbindVAOWhenSettingIndexBufferData();
void unbindVAOBeforeEnteringExternalSection(); void unbindVAOBeforeEnteringExternalSection();
@ -227,6 +233,10 @@ MeshGLTest::MeshGLTest() {
&MeshGLTest::addVertexBufferMultipleGaps, &MeshGLTest::addVertexBufferMultipleGaps,
&MeshGLTest::addVertexBufferMovedOutInstance, &MeshGLTest::addVertexBufferMovedOutInstance,
&MeshGLTest::addVertexBufferTransferOwnwership,
&MeshGLTest::addVertexBufferInstancedTransferOwnwership,
&MeshGLTest::addVertexBufferDynamicTransferOwnwership,
&MeshGLTest::addVertexBufferInstancedDynamicTransferOwnwership,
&MeshGLTest::setIndexBuffer<GL::MeshIndexType>, &MeshGLTest::setIndexBuffer<GL::MeshIndexType>,
&MeshGLTest::setIndexBuffer<Magnum::MeshIndexType>, &MeshGLTest::setIndexBuffer<Magnum::MeshIndexType>,
@ -235,6 +245,10 @@ MeshGLTest::MeshGLTest() {
&MeshGLTest::setIndexBufferUnsignedInt, &MeshGLTest::setIndexBufferUnsignedInt,
&MeshGLTest::setIndexBufferMovedOutInstance, &MeshGLTest::setIndexBufferMovedOutInstance,
&MeshGLTest::setIndexBufferTransferOwnership<GL::MeshIndexType>,
&MeshGLTest::setIndexBufferTransferOwnership<Magnum::MeshIndexType>,
&MeshGLTest::setIndexBufferRangeTransferOwnership<GL::MeshIndexType>,
&MeshGLTest::setIndexBufferRangeTransferOwnership<Magnum::MeshIndexType>,
&MeshGLTest::unbindVAOWhenSettingIndexBufferData, &MeshGLTest::unbindVAOWhenSettingIndexBufferData,
&MeshGLTest::unbindVAOBeforeEnteringExternalSection, &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"); 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 { namespace {
const Float indexedVertexData[] = { const Float indexedVertexData[] = {
0.0f, /* Offset */ 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"); CORRADE_COMPARE(out.str(), "GL::Mesh::setIndexBuffer(): empty or moved-out Buffer instance was passed\n");
} }
template<class T> void MeshGLTest::setIndexBufferTransferOwnership() {
setTestCaseName(std::is_same<T, MeshIndexType>::value ?
"setIndexBufferTransferOwnership<GL::MeshIndexType>" :
"setIndexBufferTransferOwnership<Magnum::MeshIndexType>");
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<class T> void MeshGLTest::setIndexBufferRangeTransferOwnership() {
setTestCaseName(std::is_same<T, MeshIndexType>::value ?
"setIndexBufferRangeTransferOwnership<GL::MeshIndexType>" :
"setIndexBufferRangeTransferOwnership<Magnum::MeshIndexType>");
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() { void MeshGLTest::unbindVAOWhenSettingIndexBufferData() {
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::vertex_array_object>()) if(!Context::current().isExtensionSupported<Extensions::ARB::vertex_array_object>())

Loading…
Cancel
Save