From 10e64d2d27d3f4f1c6eb1b199dbda10eb7e1754f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 21 Oct 2012 23:32:05 +0200 Subject: [PATCH] Mesh rework, part 2: reworked internals. * VAOs are used only if the extension is supported. * Removed finalize() and other already useless stuff. * Preparation for DSA and state tracking. --- src/Context.cpp | 4 ++ src/IndexedMesh.cpp | 65 ++++++++++++------ src/IndexedMesh.h | 80 +++++++++++++++------- src/Mesh.cpp | 157 ++++++++++++++++++++++++-------------------- src/Mesh.h | 132 +++++++++++++++++++++---------------- 5 files changed, 270 insertions(+), 168 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index eed515804..a4473c665 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -23,6 +23,8 @@ #include "AbstractTexture.h" #include "Buffer.h" #include "Extensions.h" +#include "IndexedMesh.h" +#include "Mesh.h" #include "Implementation/State.h" #include "BufferedTexture.h" @@ -205,6 +207,8 @@ Context::Context() { AbstractTexture::initializeContextBasedFunctionality(this); Buffer::initializeContextBasedFunctionality(this); BufferedTexture::initializeContextBasedFunctionality(this); + IndexedMesh::initializeContextBasedFunctionality(this); + Mesh::initializeContextBasedFunctionality(this); } Context::~Context() { diff --git a/src/IndexedMesh.cpp b/src/IndexedMesh.cpp index b6473c677..061e892df 100644 --- a/src/IndexedMesh.cpp +++ b/src/IndexedMesh.cpp @@ -17,21 +17,28 @@ #include +#include "Context.h" +#include "Extensions.h" + namespace Magnum { -void IndexedMesh::draw() { - /* Vertex array must be bound before finalization */ - #ifndef MAGNUM_TARGET_GLES - bind(); - #endif +IndexedMesh::CreateIndexedImplementation IndexedMesh::createIndexedImplementation = &IndexedMesh::createIndexedImplementationDefault; +IndexedMesh::BindIndexedImplementation IndexedMesh::bindIndexedImplementation = &IndexedMesh::bindIndexedImplementationDefault; + +IndexedMesh::IndexedMesh(Mesh::Primitive primitive): Mesh(primitive), _indexCount(0), _indexType(Type::UnsignedShort) { + _indexBuffer.setTargetHint(Buffer::Target::ElementArray); + + (this->*createIndexedImplementation)(); +} - finalize(); +IndexedMesh::IndexedMesh(Mesh::Primitive primitive, GLsizei vertexCount, GLsizei indexCount, Type indexType): Mesh(primitive, vertexCount), _indexCount(indexCount), _indexType(indexType) { + _indexBuffer.setTargetHint(Buffer::Target::ElementArray); - /* Buffers must be bound after initialization */ - #ifdef MAGNUM_TARGET_GLES + (this->*createIndexedImplementation)(); +} + +void IndexedMesh::draw() { bind(); - _indexBuffer.bind(Buffer::Target::ElementArray); - #endif /** @todo Start at given index */ glDrawElements(static_cast(primitive()), _indexCount, static_cast(_indexType), nullptr); @@ -39,20 +46,40 @@ void IndexedMesh::draw() { unbind(); } -#ifndef DOXYGEN_GENERATING_OUTPUT -void IndexedMesh::finalize() { - if(isFinalized()) return; - +void IndexedMesh::bind() { CORRADE_ASSERT(_indexCount, "IndexedMesh: the mesh has zero index count!", ); - /* Finalize attribute positions */ - Mesh::finalize(); + Mesh::bind(); + (this->*bindIndexedImplementation)(); +} + +void IndexedMesh::initializeContextBasedFunctionality(Context* context) { + if(context->isExtensionSupported()) { + #ifndef MAGNUM_TARGET_GLES + Debug() << "IndexedMesh: using" << Extensions::GL::APPLE::vertex_array_object::string() << "features"; + + createIndexedImplementation = &IndexedMesh::createIndexedImplementationVAO; + bindIndexedImplementation = &IndexedMesh::bindIndexedImplementationVAO; + #endif + } +} + +void IndexedMesh::createIndexedImplementationDefault() {} + +#ifndef MAGNUM_TARGET_GLES +void IndexedMesh::createIndexedImplementationVAO() { + glBindVertexArray(vao); - /* Bind index buffer to VAO too */ - #ifndef MAGNUM_TARGET_GLES _indexBuffer.bind(Buffer::Target::ElementArray); - #endif } #endif +void IndexedMesh::bindIndexedImplementationDefault() { + _indexBuffer.bind(Buffer::Target::ElementArray); +} + +#ifndef MAGNUM_TARGET_GLES +void IndexedMesh::bindIndexedImplementationVAO() {} +#endif + } diff --git a/src/IndexedMesh.h b/src/IndexedMesh.h index 541c81a7b..873a6d066 100644 --- a/src/IndexedMesh.h +++ b/src/IndexedMesh.h @@ -25,21 +25,39 @@ namespace Magnum { /** - * @brief Indexed mesh - */ +@brief Indexed mesh + +@section IndexedMesh-configuration Indexed mesh configuration + +Next to @ref Mesh-configuration "everything needed for non-indexed mesh" you +have to specify also index count and type (either in constructor or using +setIndexCount() and setIndexType()). Then fill index buffer or use +MeshTools::compressIndices() to conveniently fill the index buffer and set +index count and type. + +@section IndexedMesh-drawing Rendering meshes + +From user point-of-view the operation is the same as for +@ref Mesh-drawing "non-indexed meshes". + +@section IndexedMesh-performance-optimization Performance optimizations + +If @extension{APPLE,vertex_array_object} is supported, next to +@ref Mesh-performance-optimization "optimizations in Mesh itself" the index +buffer is bound on object construction instead of in every draw() call. +*/ class MAGNUM_EXPORT IndexedMesh: public Mesh { + friend class Context; + public: /** * @brief Implicit constructor * @param primitive Primitive type * - * Allows creating the object without knowing anything about mesh data. - * Note that you have to call setVertexCount(), setIndexCount() and - * setIndexType() manually for mesh to draw properly. + * @see @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object} + * is available) */ - inline IndexedMesh(Primitive primitive = Primitive::Triangles): Mesh(primitive), _indexCount(0), _indexType(Type::UnsignedShort) { - _indexBuffer.setTargetHint(Buffer::Target::ElementArray); - } + IndexedMesh(Primitive primitive = Primitive::Triangles); /** * @brief Constructor @@ -47,10 +65,12 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh { * @param vertexCount Count of unique vertices * @param indexCount Count of indices * @param indexType Type of indices (indexable, see TypeTraits) + * + * @see setPrimitive(), setVertexCount(), setIndexCount(), + * setIndexType(), @fn_gl{BindVertexArray} (if + * @extension{APPLE,vertex_array_object} is available) */ - inline IndexedMesh(Primitive primitive, GLsizei vertexCount, GLsizei indexCount, Type indexType = Type::UnsignedShort): Mesh(primitive, vertexCount), _indexCount(indexCount), _indexType(indexType) { - _indexBuffer.setTargetHint(Buffer::Target::ElementArray); - } + IndexedMesh(Primitive primitive, GLsizei vertexCount, GLsizei indexCount, Type indexType = Type::UnsignedShort); /** @brief Index count */ inline GLsizei indexCount() const { return _indexCount; } @@ -60,7 +80,6 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh { * @return Pointer to self (for method chaining) * * @see MeshTools::compressIndices() - * @todo definalize after that? */ inline IndexedMesh* setIndexCount(GLsizei count) { _indexCount = count; @@ -73,6 +92,8 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh { /** * @brief Set index type * @return Pointer to self (for method chaining) + * + * @see MeshTools::compressIndices() */ inline IndexedMesh* setIndexType(Type type) { _indexType = type; @@ -90,20 +111,35 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh { /** * @brief Draw the mesh * - * Expects an active shader with all uniforms set. - * @see Buffer::bind(), bind(), unbind(), finalize(), @fn_gl{DrawElements} + * Expects an active shader with all uniforms set. See + * @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" + * for more information. + * @see @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer}, + * @fn_gl{VertexAttribPointer}, @fn_gl{DisableVertexAttribArray} + * or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object} + * is available), @fn_gl{DrawElements} */ void draw(); - protected: - /** - * @brief Finalize the mesh - * - * @see Mesh::finalize(), Buffer::bind() - */ - MAGNUM_LOCAL void finalize(); - private: + static void MAGNUM_LOCAL initializeContextBasedFunctionality(Context* context); + + void MAGNUM_LOCAL bind(); + + typedef void(IndexedMesh::*CreateIndexedImplementation)(); + void MAGNUM_LOCAL createIndexedImplementationDefault(); + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL createIndexedImplementationVAO(); + #endif + static MAGNUM_LOCAL CreateIndexedImplementation createIndexedImplementation; + + typedef void(IndexedMesh::*BindIndexedImplementation)(); + void MAGNUM_LOCAL bindIndexedImplementationDefault(); + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL bindIndexedImplementationVAO(); + #endif + static MAGNUM_LOCAL BindIndexedImplementation bindIndexedImplementation; + Buffer _indexBuffer; GLsizei _indexCount; Type _indexType; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 9c91ff31b..d5931139d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -18,59 +18,38 @@ #include #include "Buffer.h" +#include "Context.h" +#include "Extensions.h" using namespace std; namespace Magnum { -Mesh::Mesh(Mesh&& other): - #ifndef MAGNUM_TARGET_GLES - vao(other.vao), - #endif - _primitive(other._primitive), _vertexCount(other._vertexCount), finalized(other.finalized), attributes(other.attributes) -{ - #ifndef MAGNUM_TARGET_GLES - other.vao = 0; - #endif -} +Mesh::CreateImplementation Mesh::createImplementation = &Mesh::createImplementationDefault; +Mesh::DestroyImplementation Mesh::destroyImplementation = &Mesh::destroyImplementationDefault; +Mesh::BindAttributeImplementation Mesh::bindAttributeImplementation = &Mesh::bindAttributeImplementationDefault; +Mesh::BindImplementation Mesh::bindImplementation = &Mesh::bindImplementationDefault; +Mesh::UnbindImplementation Mesh::unbindImplementation = &Mesh::unbindImplementationDefault; -void Mesh::destroy() { - #ifndef MAGNUM_TARGET_GLES - glDeleteVertexArrays(1, &vao); - #endif +Mesh::Mesh(Mesh&& other): vao(other.vao), _primitive(other._primitive), _vertexCount(other._vertexCount), attributes(other.attributes) { + other.vao = 0; } Mesh& Mesh::operator=(Mesh&& other) { - destroy(); + (this->*destroyImplementation)(); - #ifndef MAGNUM_TARGET_GLES vao = other.vao; - #endif _primitive = other._primitive; _vertexCount = other._vertexCount; - finalized = other.finalized; attributes = other.attributes; - #ifndef MAGNUM_TARGET_GLES other.vao = 0; - #endif return *this; } void Mesh::draw() { - /* Vertex array must be bound before finalization */ - #ifndef MAGNUM_TARGET_GLES - bind(); - #endif - - /* Finalize, if not already */ - finalize(); - - /* Buffers must be bound after initialization */ - #ifdef MAGNUM_TARGET_GLES bind(); - #endif /** @todo Start at given index */ glDrawArrays(static_cast(_primitive), 0, _vertexCount); @@ -78,65 +57,99 @@ void Mesh::draw() { unbind(); } -#ifndef DOXYGEN_GENERATING_OUTPUT void Mesh::bind() { - #ifndef MAGNUM_TARGET_GLES - glBindVertexArray(vao); - #else - bindBuffers(); - #endif -} + CORRADE_ASSERT((_vertexCount == 0) == attributes.empty(), "Mesh: vertex count is non-zero, but no attributes are bound", ); -void Mesh::unbind() { - #ifndef MAGNUM_TARGET_GLES - glBindVertexArray(0); - #else - for(const Attribute& attribute: attributes) - glDisableVertexAttribArray(attribute.location); - #endif + (this->*bindImplementation)(); } -void Mesh::finalize() { - /* Already finalized */ - if(finalized) return; +void Mesh::addVertexAttribute(Buffer* buffer, GLuint location, GLint count, Type type, GLintptr offset, GLsizei stride) { + CORRADE_ASSERT(_vertexCount != 0, "Mesh: vertex count must be set before binding attributes", ); - CORRADE_ASSERT((_vertexCount == 0) == attributes.empty(), "Mesh: vertex count is non-zero, but no attributes are bound", ); + attributes.push_back({ + buffer, + location, + count, + type, + offset, + stride + }); - finalized = true; + (this->*bindAttributeImplementation)(attributes.back()); +} + +void Mesh::vertexAttribPointer(const Mesh::Attribute& attribute) { + glEnableVertexAttribArray(attribute.location); + + attribute.buffer->bind(Buffer::Target::Array); #ifndef MAGNUM_TARGET_GLES - bindBuffers(); + if(TypeInfo::isIntegral(attribute.type)) + glVertexAttribIPointer(attribute.location, attribute.count, static_cast(attribute.type), attribute.stride, reinterpret_cast(attribute.offset)); + else #endif + glVertexAttribPointer(attribute.location, attribute.count, static_cast(attribute.type), GL_FALSE, attribute.stride, reinterpret_cast(attribute.offset)); } -void Mesh::bindBuffers() { - /* Bind all attributes to this buffer */ - for(const Attribute& attribute: attributes) { - glEnableVertexAttribArray(attribute.location); - - attribute.buffer->bind(Buffer::Target::Array); - +void Mesh::initializeContextBasedFunctionality(Context* context) { + if(context->isExtensionSupported()) { #ifndef MAGNUM_TARGET_GLES - if(TypeInfo::isIntegral(attribute.type)) - glVertexAttribIPointer(attribute.location, attribute.count, static_cast(attribute.type), attribute.stride, reinterpret_cast(attribute.offset)); - else + Debug() << "Mesh: using" << Extensions::GL::APPLE::vertex_array_object::string() << "features"; + + createImplementation = &Mesh::createImplementationVAO; + destroyImplementation = &Mesh::destroyImplementationVAO; + bindAttributeImplementation = &Mesh::bindAttributeImplementationVAO; + bindImplementation = &Mesh::bindImplementationVAO; + unbindImplementation = &Mesh::unbindImplementationVAO; #endif - glVertexAttribPointer(attribute.location, attribute.count, static_cast(attribute.type), GL_FALSE, attribute.stride, reinterpret_cast(attribute.offset)); } } + +void Mesh::createImplementationDefault() {} + +#ifndef MAGNUM_TARGET_GLES +void Mesh::createImplementationVAO() { + glGenVertexArrays(1, &vao); +} #endif -void Mesh::addVertexAttribute(Buffer* buffer, GLuint location, GLint count, Type type, GLintptr offset, GLsizei stride) { - CORRADE_ASSERT(_vertexCount != 0, "Mesh: vertex count must be set before binding attributes", ); +void Mesh::destroyImplementationDefault() {} - attributes.push_back({ - buffer, - location, - count, - type, - offset, - stride - }); +#ifndef MAGNUM_TARGET_GLES +void Mesh::destroyImplementationVAO() { + glDeleteVertexArrays(1, &vao); } +#endif + +void Mesh::bindAttributeImplementationDefault(const Attribute&) {} + +#ifndef MAGNUM_TARGET_GLES +void Mesh::bindAttributeImplementationVAO(const Attribute& attribute) { + glBindVertexArray(vao); + vertexAttribPointer(attribute); +} +#endif + +void Mesh::bindImplementationDefault() { + for(const Attribute& attribute: attributes) + vertexAttribPointer(attribute); +} + +#ifndef MAGNUM_TARGET_GLES +void Mesh::bindImplementationVAO() { + glBindVertexArray(vao); +} +#endif + +void Mesh::unbindImplementationDefault() { + for(const Attribute& attribute: attributes) + glDisableVertexAttribArray(attribute.location); +} + +#ifndef MAGNUM_TARGET_GLES +void Mesh::unbindImplementationVAO() { + glBindVertexArray(0); +} +#endif } diff --git a/src/Mesh.h b/src/Mesh.h index 87f69c521..b229cc835 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -27,6 +27,7 @@ namespace Magnum { class Buffer; +class Context; /** @brief Base class for managing non-indexed meshes @@ -77,8 +78,12 @@ mesh->setPrimitive(plane.primitive()) Basic workflow is to set up respective shader (see @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" for more infromation) and call Mesh::draw(). -VAOs are used for desktop OpenGL (not in OpenGL ES). -@requires_gl30 Extension @extension{APPLE,vertex_array_object} +@section Mesh-performance-optimization Performance optimizations + +If @extension{APPLE,vertex_array_object} is supported, VAOs are used instead +of binding the buffers and specifying vertex attribute pointers in each +draw() call. + @requires_gl30 Extension @extension{EXT,gpu_shader4} (for unsigned integer attributes) @todo Support for normalized values (e.g. for color as char[4] passed to @@ -90,6 +95,9 @@ VAOs are used for desktop OpenGL (not in OpenGL ES). @todo Redo in a way that allows glMultiDrawArrays, glDrawArraysInstanced etc. */ class MAGNUM_EXPORT Mesh { + friend class IndexedMesh; + friend class Context; + Mesh(const Mesh& other) = delete; Mesh& operator=(const Mesh& other) = delete; @@ -331,12 +339,11 @@ class MAGNUM_EXPORT Mesh { * @param primitive Primitive type * @param vertexCount Vertex count * - * @see @fn_gl{GenVertexArrays}, setPrimitive(), setVertexCount() + * @see setPrimitive(), setVertexCount(), @fn_gl{GenVertexArrays} (if + * @extension{APPLE,vertex_array_object} is available) */ - inline Mesh(Primitive primitive = Primitive::Triangles, GLsizei vertexCount = 0): _primitive(primitive), _vertexCount(vertexCount), finalized(false) { - #ifndef MAGNUM_TARGET_GLES - glGenVertexArrays(1, &vao); - #endif + inline Mesh(Primitive primitive = Primitive::Triangles, GLsizei vertexCount = 0): _primitive(primitive), _vertexCount(vertexCount) { + (this->*createImplementation)(); } /** @brief Move constructor */ @@ -345,20 +352,16 @@ class MAGNUM_EXPORT Mesh { /** * @brief Destructor * - * @see @fn_gl{DeleteVertexArrays} + * @see @fn_gl{DeleteVertexArrays} (if + * @extension{APPLE,vertex_array_object} is available) */ - inline virtual ~Mesh() { destroy(); } + inline virtual ~Mesh() { + (this->*destroyImplementation)(); + } /** @brief Move assignment */ Mesh& operator=(Mesh&& other); - /** - * @brief Whether the mesh is finalized - * - * When the mesh is finalized, no new attributes can be bound. - */ - inline bool isFinalized() const { return finalized; } - /** @brief Primitive type */ inline Primitive primitive() const { return _primitive; } @@ -385,7 +388,6 @@ class MAGNUM_EXPORT Mesh { */ inline Mesh* setVertexCount(GLsizei vertexCount) { _vertexCount = vertexCount; - finalized = false; attributes.clear(); return this; } @@ -428,7 +430,10 @@ class MAGNUM_EXPORT Mesh { * mesh, you must ensure it will exist for whole lifetime of the * mesh and delete it afterwards. * - * @see addInterleavedVertexBuffer() + * @see addInterleavedVertexBuffer(), @fn_gl{BindVertexArray}, + * @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer}, + * @fn_gl{VertexAttribPointer} (if + * @extension{APPLE,vertex_array_object} is available) */ template inline Mesh* addVertexBuffer(Buffer* buffer, const T&... attributes) { addVertexBufferInternal(buffer, 0, attributes...); @@ -485,7 +490,10 @@ class MAGNUM_EXPORT Mesh { * mesh, you must ensure it will exist for whole lifetime of the * mesh and delete it afterwards. * - * @see addVertexBufferStride(), addVertexBuffer() + * @see addVertexBufferStride(), addVertexBuffer(), + * @fn_gl{BindVertexArray}, @fn_gl{EnableVertexAttribArray}, + * @fn_gl{BindBuffer}, @fn_gl{VertexAttribPointer} (if + * @extension{APPLE,vertex_array_object} is available) */ template inline Mesh* addInterleavedVertexBuffer(Buffer* buffer, GLintptr offset, const T&... attributes) { addInterleavedVertexBufferInternal(buffer, offset, strideOfInterleaved(attributes...), attributes...); @@ -505,42 +513,16 @@ class MAGNUM_EXPORT Mesh { /** * @brief Draw the mesh * - * Expects an active shader with all uniforms set. - * @see bind(), unbind(), finalize(), @fn_gl{DrawArrays} + * Expects an active shader with all uniforms set. See + * @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" + * for more information. + * @see @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer}, + * @fn_gl{VertexAttribPointer}, @fn_gl{DisableVertexAttribArray} + * or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object} + * is available), @fn_gl{DrawArrays} */ virtual void draw(); - protected: - /** - * @brief Bind all buffers - * - * @see @fn_gl{EnableVertexAttribArray}, @fn_gl{VertexAttribPointer} - */ - void bindBuffers(); - - /** - * @brief Bind vertex array or all buffers - * - * @see @fn_gl{BindVertexArray} or bindBuffers() - */ - void bind(); - - /** - * @brief Unbind vertex array or all buffers - * - * @see @fn_gl{BindVertexArray} or @fn_gl{DisableVertexAttribArray} - */ - void unbind(); - - /** - * @brief Finalize the mesh - * - * Computes location and stride of each attribute in its buffer. After - * this function is called, no new attribute can be bound. - * @see bindBuffers() - */ - MAGNUM_LOCAL void finalize(); - private: struct MAGNUM_LOCAL Attribute { Buffer* buffer; @@ -551,6 +533,8 @@ class MAGNUM_EXPORT Mesh { GLsizei stride; }; + static void MAGNUM_LOCAL initializeContextBasedFunctionality(Context* context); + /* Adding non-interleaved vertex attributes */ template inline void addVertexBufferInternal(Buffer* buffer, GLintptr offset, const AbstractShaderProgram::Attribute&, const U&... attributes) { addVertexAttribute(buffer, location, TypeTraits::count(), TypeTraits::type(), offset, 0); @@ -588,14 +572,52 @@ class MAGNUM_EXPORT Mesh { void MAGNUM_EXPORT addVertexAttribute(Buffer* buffer, GLuint location, GLint count, Type type, GLintptr offset, GLsizei stride); - void destroy(); + void MAGNUM_LOCAL bind(); + + inline void unbind() { + (this->*unbindImplementation)(); + } + void MAGNUM_LOCAL vertexAttribPointer(const Attribute& attribute); + + typedef void(Mesh::*CreateImplementation)(); + void MAGNUM_LOCAL createImplementationDefault(); #ifndef MAGNUM_TARGET_GLES - GLuint vao; + void MAGNUM_LOCAL createImplementationVAO(); + #endif + static CreateImplementation createImplementation; + + typedef void(Mesh::*DestroyImplementation)(); + void MAGNUM_LOCAL destroyImplementationDefault(); + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL destroyImplementationVAO(); #endif + static DestroyImplementation destroyImplementation; + + typedef void(Mesh::*BindAttributeImplementation)(const Attribute&); + void MAGNUM_LOCAL bindAttributeImplementationDefault(const Attribute& attribute); + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL bindAttributeImplementationVAO(const Attribute& attribute); + #endif + static MAGNUM_LOCAL BindAttributeImplementation bindAttributeImplementation; + + typedef void(Mesh::*BindImplementation)(); + void MAGNUM_LOCAL bindImplementationDefault(); + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL bindImplementationVAO(); + #endif + static MAGNUM_LOCAL BindImplementation bindImplementation; + + typedef void(Mesh::*UnbindImplementation)(); + void MAGNUM_LOCAL unbindImplementationDefault(); + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL unbindImplementationVAO(); + #endif + static MAGNUM_LOCAL UnbindImplementation unbindImplementation; + + GLuint vao; Primitive _primitive; GLsizei _vertexCount; - bool finalized; std::vector attributes; };