From 20df20de0f6891b00738169a1435df4163e30ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 27 Apr 2014 17:17:25 +0200 Subject: [PATCH] Support for instanced mesh drawing and base instance. On desktop GL this is provided by ARB_draw_instanced (GL 3.1). Base instance is available only on desktop GL (4.2, ARB_base_instance). In ES2 the instanced functionality is provided by three (!) different extensions (ANGLE_instanced_arrays, EXT_draw_instanced, NV_draw_instanced), the proper implementation is chosen on context creation based on what extension is available. Though we don't have extension loader for ES yet, thus all these extensions are disabled and the implementation has assertion in it. Added blind test which tests only that something has been drawn and no errors were emitted, but not whether the right command is used. I'll probably need to check this later, because the Mesh::draw() behemoth is going slightly out of hand :) --- doc/opengl-mapping.dox | 3 +- doc/opengl-support.dox | 9 +- src/Magnum/Context.cpp | 3 + src/Magnum/Extensions.h | 5 + src/Magnum/Implementation/MeshState.cpp | 26 ++ src/Magnum/Implementation/MeshState.h | 5 + .../Implementation/setupDriverWorkarounds.cpp | 3 + src/Magnum/Mesh.cpp | 201 ++++++++++++--- src/Magnum/Mesh.h | 103 ++++++-- src/Magnum/MeshView.cpp | 16 +- src/Magnum/MeshView.h | 48 +++- src/Magnum/Test/MeshGLTest.cpp | 239 +++++++++++++++++- 12 files changed, 585 insertions(+), 76 deletions(-) diff --git a/doc/opengl-mapping.dox b/doc/opengl-mapping.dox index 2014e2859..7c82f8f40 100644 --- a/doc/opengl-mapping.dox +++ b/doc/opengl-mapping.dox @@ -103,9 +103,8 @@ OpenGL function | Matching API @fn_gl{DetachShader} | | @fn_gl{DispatchCompute} | | @fn_gl{DispatchComputeIndirect} | | -@fn_gl{DrawArrays}, \n @fn_gl{DrawElements}, \n @fn_gl{DrawRangeElements}, \n @fn_gl{DrawElementsBaseVertex}, \n @fn_gl{DrawRangeElementsBaseVertex} | @ref Mesh::draw(), \n @ref MeshView::draw() +@fn_gl{DrawArrays}, \n @fn_gl{DrawArraysInstanced}, \n @fn_gl{DrawArraysInstancedBaseInstance}, \n @fn_gl{DrawElements}, \n @fn_gl{DrawRangeElements}, \n @fn_gl{DrawElementsBaseVertex}, \n @fn_gl{DrawRangeElementsBaseVertex}, \n @fn_gl{DrawElementsInstanced}, \n @fn_gl{DrawElementsInstancedBaseInstance}, \n @fn_gl{DrawElementsInstancedBaseVertex}, \n @fn_gl{DrawElementsInstancedBaseVertexBaseInstance} | @ref Mesh::draw(), \n @ref MeshView::draw() @fn_gl{DrawArraysIndirect}, \n @fn_gl{DrawElementsIndirect}, \n @fn_gl{MultiDrawArraysIndirect}, \n @fn_gl{MultiDrawElementsIndirect} | | -@fn_gl{DrawArraysInstanced}, \n @fn_gl{DrawArraysInstancedBaseInstance}, \n @fn_gl{DrawElementsInstanced}, \n @fn_gl{DrawElementsInstancedBaseInstance}, \n @fn_gl{DrawElementsInstancedBaseVertex}, \n @fn_gl{DrawElementsInstancedBaseVertexBaseInstance} | | @fn_gl{DrawBuffer}, \n @fn_gl_extension{FramebufferDrawBuffers,EXT,direct_state_access}, \n @fn_gl{DrawBuffers}, \n @fn_gl_extension{FramebufferDrawBuffers,EXT,direct_state_access} | @ref DefaultFramebuffer::mapForDraw(), \n @ref Framebuffer::mapForDraw() @fn_gl{DrawTransformFeedback}, \n @fn_gl{DrawTransformFeedbackInstanced}, \n @fn_gl{DrawTransformFeedbackStream}, \n @fn_gl{DrawTransformFeedbackStreamInstanced} | | @fn_gl{Enable}, `glDisable()` | @ref Renderer::setFeature() diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index 25ba6a715..cc16ada03 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -82,7 +82,7 @@ following: %Extension | Status -------------------------------------------- | ------ @extension{ARB,texture_rectangle} | done -@extension{ARB,draw_instanced} | | +@extension{ARB,draw_instanced} | done @extension{ARB,texture_buffer_object} | done @extension{ARB,uniform_buffer_object} | | @extension{ARB,copy_buffer} | done @@ -95,7 +95,7 @@ following: -------------------------------------------- | ------ @extension{ARB,geometry_shader4} | missing layered attachments @extension{ARB,depth_clamp} | done -@extension{ARB,draw_elements_base_vertex} | | +@extension{ARB,draw_elements_base_vertex} | missing `Multi*` command @extension{ARB,fragment_coord_conventions} | done (shading language only) @extension{ARB,provoking_vertex} | done @extension{ARB,seamless_cube_map} | done @@ -152,7 +152,7 @@ following: %Extension | Status -------------------------------------------- | ------ @extension{ARB,texture_compression_bptc} | done -@extension{ARB,base_instance} | | +@extension{ARB,base_instance} | done @extension{ARB,shading_language_420pack} | done (shading language only) @extension{ARB,transform_feedback_instanced} | | @extension{ARB,compressed_texture_pixel_storage} | | @@ -243,6 +243,7 @@ supported. -------------------------------------------- | ------ @es_extension{ANGLE,framebuffer_blit} | done @es_extension{ANGLE,framebuffer_multisample} | done +@es_extension{ANGLE,instanced_arrays} | missing vertex attrib divisor @es_extension{ANGLE,depth_texture} | done @es_extension{APPLE,framebuffer_multisample} | done (ES 3.0 subset) @es_extension{APPLE,texture_max_level} | done @@ -256,9 +257,11 @@ supported. @es_extension{EXT,texture_rg} | done @es_extension{EXT,texture_storage} | done @es_extension{EXT,map_buffer_range} | done +@es_extension2{EXT,draw_instanced,draw_instanced} | done @es_extension{NV,draw_buffers} | done @es_extension{NV,fbo_color_attachments} | done @es_extension{NV,read_buffer} | done +@es_extension{NV,draw_instanced} | done @es_extension{NV,framebuffer_blit} | done @es_extension{NV,framebuffer_multisample} | done @es_extension{NV,shadow_samplers_array} | done (shading language only) diff --git a/src/Magnum/Context.cpp b/src/Magnum/Context.cpp index 58c3c5cb2..c8847bc3d 100644 --- a/src/Magnum/Context.cpp +++ b/src/Magnum/Context.cpp @@ -212,6 +212,7 @@ const std::vector& Extension::extensions(Version version) { static const std::vector extensionsES300{ _extension(GL,ANGLE,framebuffer_blit), _extension(GL,ANGLE,framebuffer_multisample), + _extension(GL,ANGLE,instanced_arrays), _extension(GL,ANGLE,depth_texture), _extension(GL,APPLE,framebuffer_multisample), _extension(GL,APPLE,texture_max_level), @@ -225,9 +226,11 @@ const std::vector& Extension::extensions(Version version) { _extension(GL,EXT,texture_rg), _extension(GL,EXT,texture_storage), _extension(GL,EXT,map_buffer_range), + _extension(GL,EXT,draw_instanced), _extension(GL,NV,draw_buffers), _extension(GL,NV,fbo_color_attachments), _extension(GL,NV,read_buffer), + _extension(GL,NV,draw_instanced), _extension(GL,NV,framebuffer_blit), _extension(GL,NV,framebuffer_multisample), _extension(GL,NV,shadow_samplers_array), diff --git a/src/Magnum/Extensions.h b/src/Magnum/Extensions.h index 4df94b3fa..c41039c9f 100644 --- a/src/Magnum/Extensions.h +++ b/src/Magnum/Extensions.h @@ -205,6 +205,7 @@ namespace GL { #ifdef MAGNUM_TARGET_GLES2 _extension(GL,ANGLE,framebuffer_blit, GLES200, GLES300) // #83 _extension(GL,ANGLE,framebuffer_multisample, GLES200, GLES300) // #84 + _extension(GL,ANGLE,instanced_arrays, GLES200, GLES300) // #109 _extension(GL,ANGLE,depth_texture, GLES200, GLES300) // #138 #endif } namespace APPLE { @@ -253,6 +254,9 @@ namespace GL { _extension(GL,EXT,map_buffer_range, GLES200, GLES300) // #121 #endif _extension(GL,EXT,disjoint_timer_query, GLES200, None) // #150 + #ifdef MAGNUM_TARGET_GLES2 + _extension(GL,EXT,draw_instanced, GLES200, GLES300) // #157 + #endif #ifndef MAGNUM_TARGET_GLES2 _extension(GL,EXT,shader_integer_mix, GLES300, None) // #161 #endif @@ -269,6 +273,7 @@ namespace GL { _extension(GL,NV,read_stencil, GLES200, None) // #94 _extension(GL,NV,read_depth_stencil, GLES200, None) // #94 #ifdef MAGNUM_TARGET_GLES2 + _extension(GL,NV,draw_instanced, GLES200, GLES300) // #141 _extension(GL,NV,framebuffer_blit, GLES200, GLES300) // #142 _extension(GL,NV,framebuffer_multisample, GLES200, GLES300) // #143 _extension(GL,NV,shadow_samplers_array, GLES200, GLES300) // #146 diff --git a/src/Magnum/Implementation/MeshState.cpp b/src/Magnum/Implementation/MeshState.cpp index 3c9ac875a..b7946673f 100644 --- a/src/Magnum/Implementation/MeshState.cpp +++ b/src/Magnum/Implementation/MeshState.cpp @@ -92,6 +92,32 @@ MeshState::MeshState(Context& context, std::vector& extensions): cu unbindImplementation = &Mesh::unbindImplementationDefault; } #endif + + #ifdef MAGNUM_TARGET_GLES2 + /* Instanced draw ímplementation on ES2 */ + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::GL::ANGLE::instanced_arrays::string()); + + drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationANGLE; + drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationANGLE; + + } else if(context.isExtensionSupported()) { + extensions.push_back(Extensions::GL::EXT::draw_instanced::string()); + + drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationEXT; + drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationEXT; + + } else if(context.isExtensionSupported()) { + extensions.push_back(Extensions::GL::NV::draw_instanced::string()); + + drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationNV; + drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationNV; + + } else { + drawArraysInstancedImplementation = nullptr; + drawElementsInstancedImplementation = nullptr; + } + #endif } }} diff --git a/src/Magnum/Implementation/MeshState.h b/src/Magnum/Implementation/MeshState.h index ea4885b4f..f91dd4e5c 100644 --- a/src/Magnum/Implementation/MeshState.h +++ b/src/Magnum/Implementation/MeshState.h @@ -48,6 +48,11 @@ struct MeshState { void(Mesh::*bindImplementation)(); void(Mesh::*unbindImplementation)(); + #ifdef MAGNUM_TARGET_GLES2 + void(Mesh::*drawArraysInstancedImplementation)(GLint, GLsizei, GLsizei); + void(Mesh::*drawElementsInstancedImplementation)(GLsizei, GLintptr, GLsizei); + #endif + GLuint currentVAO; #ifndef MAGNUM_TARGET_GLES2 GLint maxElementsIndices, maxElementsVertices; diff --git a/src/Magnum/Implementation/setupDriverWorkarounds.cpp b/src/Magnum/Implementation/setupDriverWorkarounds.cpp index 7d5d7f8be..08f311c96 100644 --- a/src/Magnum/Implementation/setupDriverWorkarounds.cpp +++ b/src/Magnum/Implementation/setupDriverWorkarounds.cpp @@ -68,6 +68,7 @@ void Context::setupDriverWorkarounds() { #ifdef MAGNUM_TARGET_GLES2 _setRequiredVersion(GL::ANGLE::framebuffer_blit, None); _setRequiredVersion(GL::ANGLE::framebuffer_multisample, None); + _setRequiredVersion(GL::ANGLE::instanced_arrays, None); _setRequiredVersion(GL::APPLE::framebuffer_multisample, None); _setRequiredVersion(GL::EXT::discard_framebuffer, None); _setRequiredVersion(GL::EXT::blend_minmax, None); @@ -76,9 +77,11 @@ void Context::setupDriverWorkarounds() { #endif _setRequiredVersion(GL::EXT::texture_storage, None); _setRequiredVersion(GL::EXT::map_buffer_range, None); + _setRequiredVersion(GL::EXT::draw_instanced, None); _setRequiredVersion(GL::NV::draw_buffers, None); _setRequiredVersion(GL::NV::fbo_color_attachments, None); // ?? _setRequiredVersion(GL::NV::read_buffer, None); + _setRequiredVersion(GL::NV::draw_instanced, None); _setRequiredVersion(GL::NV::framebuffer_blit, None); _setRequiredVersion(GL::NV::framebuffer_multisample, None); _setRequiredVersion(GL::OES::texture_3D, None); diff --git a/src/Magnum/Mesh.cpp b/src/Magnum/Mesh.cpp index 53da906a2..422f32d16 100644 --- a/src/Magnum/Mesh.cpp +++ b/src/Magnum/Mesh.cpp @@ -72,11 +72,14 @@ std::size_t Mesh::indexSize(IndexType type) { CORRADE_ASSERT_UNREACHABLE(); } -Mesh::Mesh(MeshPrimitive primitive): _primitive(primitive), _count(0), _baseVertex(0) +Mesh::Mesh(MeshPrimitive primitive): _primitive(primitive), _count(0), _baseVertex(0), _instanceCount{1}, + #ifndef MAGNUM_TARGET_GLES + _baseInstance{0}, + #endif #ifndef MAGNUM_TARGET_GLES2 - , _indexStart(0), _indexEnd(0) + _indexStart(0), _indexEnd(0), #endif - , _indexOffset(0), _indexType(IndexType::UnsignedInt), _indexBuffer(nullptr) + _indexOffset(0), _indexType(IndexType::UnsignedInt), _indexBuffer(nullptr) { (this->*Context::current()->state().mesh->createImplementation)(); } @@ -92,11 +95,14 @@ Mesh::~Mesh() { (this->*Context::current()->state().mesh->destroyImplementation)(); } -Mesh::Mesh(Mesh&& other) noexcept: _id(other._id), _primitive(other._primitive), _count(other._count), _baseVertex{other._baseVertex} +Mesh::Mesh(Mesh&& other) noexcept: _id(other._id), _primitive(other._primitive), _count(other._count), _baseVertex{other._baseVertex}, _instanceCount{other._instanceCount}, + #ifndef MAGNUM_TARGET_GLES + _baseInstance{other._baseInstance}, + #endif #ifndef MAGNUM_TARGET_GLES2 - , _indexStart(other._indexStart), _indexEnd(other._indexEnd) + _indexStart(other._indexStart), _indexEnd(other._indexEnd), #endif - , _indexOffset(other._indexOffset), _indexType(other._indexType), _indexBuffer(other._indexBuffer), _attributes(std::move(other._attributes)) + _indexOffset(other._indexOffset), _indexType(other._indexType), _indexBuffer(other._indexBuffer), _attributes(std::move(other._attributes)) #ifndef MAGNUM_TARGET_GLES2 , _integerAttributes(std::move(other._integerAttributes)) #ifndef MAGNUM_TARGET_GLES @@ -112,6 +118,10 @@ Mesh& Mesh::operator=(Mesh&& other) noexcept { std::swap(_primitive, other._primitive); std::swap(_count, other._count); std::swap(_baseVertex, other._baseVertex); + std::swap(_instanceCount, other._instanceCount); + #ifndef MAGNUM_TARGET_GLES + std::swap(_baseInstance, other._baseInstance); + #endif #ifndef MAGNUM_TARGET_GLES2 std::swap(_indexStart, other._indexStart); std::swap(_indexEnd, other._indexEnd); @@ -167,50 +177,111 @@ Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, IndexType type, Unsi return *this; } -#ifndef MAGNUM_TARGET_GLES2 -void Mesh::drawInternal(Int count, Int baseVertex, GLintptr indexOffset, Int indexStart, Int indexEnd) +#ifndef MAGNUM_TARGET_GLES +void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, UnsignedInt baseInstance, GLintptr indexOffset, Int indexStart, Int indexEnd) +#elif !defined(MAGNUM_TARGET_GLES2) +void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset, Int indexStart, Int indexEnd) #else -void Mesh::drawInternal(Int count, Int baseVertex, GLintptr indexOffset) +void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset) #endif { - /* Nothing to draw */ - if(!count) return; - - (this->*Context::current()->state().mesh->bindImplementation)(); - /* Non-indexed mesh */ - if(!_indexBuffer) { - glDrawArrays(GLenum(_primitive), baseVertex, count); + const Implementation::MeshState& state = *Context::current()->state().mesh; - /* Indexed mesh with base vertex */ - } else if(baseVertex) { - #ifndef MAGNUM_TARGET_GLES - /* Indexed mesh with specified range */ - if(indexEnd) { - glDrawRangeElementsBaseVertex(GLenum(_primitive), indexStart, indexEnd, count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); - - /* Indexed mesh without specified range */ - } else glDrawElementsBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); - #else - CORRADE_ASSERT(false, "Mesh::draw(): desktop OpenGL is required for base vertex specification in indexed meshes", ); - #endif + /* Nothing to draw */ + if(!count || !instanceCount) return; + + (this->*state.bindImplementation)(); + + /* Non-instanced mesh */ + if(instanceCount == 1) { + /* Non-indexed mesh */ + if(!_indexBuffer) { + glDrawArrays(GLenum(_primitive), baseVertex, count); + + /* Indexed mesh with base vertex */ + } else if(baseVertex) { + #ifndef MAGNUM_TARGET_GLES + /* Indexed mesh with specified range */ + if(indexEnd) { + glDrawRangeElementsBaseVertex(GLenum(_primitive), indexStart, indexEnd, count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); + + /* Indexed mesh */ + } else glDrawElementsBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), baseVertex); + #else + CORRADE_ASSERT(false, "Mesh::draw(): desktop OpenGL is required for base vertex specification in indexed meshes", ); + #endif + + /* Indexed mesh */ + } else { + #ifndef MAGNUM_TARGET_GLES2 + /* Indexed mesh with specified range */ + if(indexEnd) { + glDrawRangeElements(GLenum(_primitive), indexStart, indexEnd, count, GLenum(_indexType), reinterpret_cast(indexOffset)); + + /* Indexed mesh */ + } else + #endif + { + glDrawElements(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset)); + } + } - /* Indexed mesh without base vertex */ + /* Instanced mesh */ } else { - /* Indexed mesh with specified range */ - #ifndef MAGNUM_TARGET_GLES2 - if(indexEnd) { - glDrawRangeElements(GLenum(_primitive), indexStart, indexEnd, count, GLenum(_indexType), reinterpret_cast(indexOffset)); - - /* Indexed mesh without specified range */ - } else - #endif - { - glDrawElements(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset)); + /* Non-indexed mesh */ + if(!_indexBuffer) { + #ifndef MAGNUM_TARGET_GLES + /* Non-indexed mesh with base instance */ + if(baseInstance) { + glDrawArraysInstancedBaseInstance(GLenum(_primitive), baseVertex, count, instanceCount, baseInstance); + + /* Non-indexed mesh */ + } else + #endif + { + #ifndef MAGNUM_TARGET_GLES2 + glDrawArraysInstanced(GLenum(_primitive), baseVertex, count, instanceCount); + #else + (this->*state.drawArraysInstancedImplementation)(baseVertex, count, instanceCount); + #endif + } + + /* Indexed mesh with base vertex */ + } else if(baseVertex) { + #ifndef MAGNUM_TARGET_GLES + /* Indexed mesh with base vertex and base instance */ + if(baseInstance) + glDrawElementsInstancedBaseVertexBaseInstance(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseVertex, baseInstance); + + /* Indexed mesh with base vertex */ + else + glDrawElementsInstancedBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseVertex); + #else + CORRADE_ASSERT(false, "Mesh::draw(): desktop OpenGL is required for base vertex specification in indexed meshes", ); + #endif + + /* Indexed mesh */ + } else { + #ifndef MAGNUM_TARGET_GLES + /* Indexed mesh with base instance */ + if(baseInstance) { + glDrawElementsInstancedBaseInstance(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount, baseInstance); + + /* Instanced mesh */ + } else + #endif + { + #ifndef MAGNUM_TARGET_GLES2 + glDrawElementsInstanced(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); + #else + (this->*state.drawElementsInstancedImplementation)(count, indexOffset, instanceCount); + #endif + } } } - (this->*Context::current()->state().mesh->unbindImplementation)(); + (this->*state.unbindImplementation)(); } void Mesh::bindVAO(GLuint vao) { @@ -400,6 +471,56 @@ void Mesh::unbindImplementationDefault() { void Mesh::unbindImplementationVAO() {} +#ifdef MAGNUM_TARGET_GLES2 +void Mesh::drawArraysInstancedImplementationANGLE(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { + //glDrawArraysInstancedANGLE(GLenum(_primitive), baseVertex, count, instanceCount); + CORRADE_INTERNAL_ASSERT(false); + static_cast(baseVertex); + static_cast(count); + static_cast(instanceCount); +} + +void Mesh::drawArraysInstancedImplementationEXT(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { + //glDrawArraysInstancedEXT(GLenum(_primitive), baseVertex, count, instanceCount); + CORRADE_INTERNAL_ASSERT(false); + static_cast(baseVertex); + static_cast(count); + static_cast(instanceCount); +} + +void Mesh::drawArraysInstancedImplementationNV(const GLint baseVertex, const GLsizei count, const GLsizei instanceCount) { + //glDrawArraysInstancedNV(GLenum(_primitive), baseVertex, count, instanceCount); + CORRADE_INTERNAL_ASSERT(false); + static_cast(baseVertex); + static_cast(count); + static_cast(instanceCount); +} + +void Mesh::drawElementsInstancedImplementationANGLE(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { + //glDrawElementsInstancedANGLE(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); + CORRADE_INTERNAL_ASSERT(false); + static_cast(count); + static_cast(indexOffset); + static_cast(instanceCount); +} + +void Mesh::drawElementsInstancedImplementationEXT(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { + //glDrawElementsInstancedEXT(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); + CORRADE_INTERNAL_ASSERT(false); + static_cast(count); + static_cast(indexOffset); + static_cast(instanceCount); +} + +void Mesh::drawElementsInstancedImplementationNV(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) { + //glDrawElementsInstancedNV(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast(indexOffset), instanceCount); + CORRADE_INTERNAL_ASSERT(false); + static_cast(count); + static_cast(indexOffset); + static_cast(instanceCount); +} +#endif + #ifndef DOXYGEN_GENERATING_OUTPUT Debug operator<<(Debug debug, MeshPrimitive value) { switch(value) { diff --git a/src/Magnum/Mesh.h b/src/Magnum/Mesh.h index ce9142d03..080d85bcd 100644 --- a/src/Magnum/Mesh.h +++ b/src/Magnum/Mesh.h @@ -143,8 +143,8 @@ 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. -If vertex/index count is zero, the mesh is empty and no draw commands are -issued when calling @ref draw(). +If vertex/index count or instance count is zero, the mesh is empty and no draw +commands are issued when calling @ref draw(). @subsection Mesh-configuration-examples Example mesh configuration @@ -331,8 +331,6 @@ calls to @fn_gl{BindBuffer} and @fn_gl{BindVertexArray}. See documentation of If index range is specified in @ref setIndexBuffer(), range-based version of drawing commands are used on desktop OpenGL and OpenGL ES 3.0. See also @ref draw() for more information. - -@todo Redo in a way that allows glMultiDrawArrays, glDrawArraysInstanced etc. */ class MAGNUM_EXPORT Mesh: public AbstractObject { friend class MeshView; @@ -567,6 +565,44 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { CORRADE_DEPRECATED("use setCount() instead") Mesh& setIndexCount(Int count) { return setCount(count); } #endif + /** @brief Instance count */ + Int instanceCount() const { return _instanceCount; } + + /** + * @brief Set instance count + * @return Reference to self (for method chaining) + * + * If set to `1`, non-instanced draw commands are issued when calling + * @ref draw(). If set to `0`, no draw commands are issued altogether. + * Default is `1`. + * @requires_gl31 %Extension @extension{ARB,draw_instanced} + * @requires_gles30 %Extension @es_extension{ANGLE,instanced_arrays}, + * @es_extension2{EXT,draw_instanced,draw_instanced} or + * @es_extension{NV,draw_instanced} in OpenGL ES 2.0. + */ + Mesh& setInstanceCount(Int count) { + _instanceCount = count; + return *this; + } + + #ifndef MAGNUM_TARGET_GLES + /** @brief Base instance */ + UnsignedInt baseInstance() const { return _baseInstance; } + + /** + * @brief Set base instance + * @return Reference to self (for method chaining) + * + * Default is `0`. + * @requires_gl42 %Extension @extension{ARB,base_instance} + * @requires_gl Base instance cannot be specified in OpenGL ES. + */ + Mesh& setBaseInstance(UnsignedInt baseInstance) { + _baseInstance = baseInstance; + return *this; + } + #endif + /** * @brief Add buffer with (interleaved) vertex attributes for use with given shader * @return Reference to self (for method chaining) @@ -680,23 +716,31 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { * @param shader Shader to use for drawing * * Expects that the shader is compatible with this mesh and is fully - * set up. See also + * set up. If vertex/index count or instance count is `0`, no draw + * commands are issued. See also * @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" * for more information. - * @see @fn_gl{UseProgram}, @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} - * or @fn_gl{DrawElements}/@fn_gl{DrawRangeElements}/ - * @fn_gl{DrawElementsBaseVertex}/@fn_gl{DrawRangeElementsBaseVertex} + * @see @ref setCount(), @ref setInstanceCount(), @fn_gl{UseProgram}, + * @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}/@fn_gl{DrawArraysInstanced}/ + * @fn_gl{DrawArraysInstancedBaseInstance} or @fn_gl{DrawElements}/ + * @fn_gl{DrawRangeElements}/@fn_gl{DrawElementsBaseVertex}/ + * @fn_gl{DrawRangeElementsBaseVertex}/@fn_gl{DrawElementsInstanced}/ + * @fn_gl{DrawElementsInstancedBaseInstance}/ + * @fn_gl{DrawElementsInstancedBaseVertex}/ + * @fn_gl{DrawElementsInstancedBaseVertexBaseInstance} */ void draw(AbstractShaderProgram& shader) { shader.use(); - #ifndef MAGNUM_TARGET_GLES2 - drawInternal(_count, _baseVertex, _indexOffset, _indexStart, _indexEnd); + #ifndef MAGNUM_TARGET_GLES + drawInternal(_count, _baseVertex, _instanceCount, _baseInstance, _indexOffset, _indexStart, _indexEnd); + #elif !defined(MAGNUM_TARGET_GLES2) + drawInternal(_count, _baseVertex, _instanceCount, _indexOffset, _indexStart, _indexEnd); #else - drawInternal(_count, _baseVertex, _indexOffset); + drawInternal(_count, _baseVertex, _instanceCount, _indexOffset); #endif } @@ -709,10 +753,12 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { * instead. */ CORRADE_DEPRECATED("use draw(AbstractShaderProgram&) instead") void draw() { - #ifndef MAGNUM_TARGET_GLES2 - drawInternal(_count, _baseVertex, _indexOffset, _indexStart, _indexEnd); + #ifndef MAGNUM_TARGET_GLES + drawInternal(_count, _baseVertex, _instanceCount, _baseInstance, _indexOffset, _indexStart, _indexEnd); + #elif !defined(MAGNUM_TARGET_GLES2) + drawInternal(_count, _baseVertex, _instanceCount, _indexOffset, _indexStart, _indexEnd); #else - drawInternal(_count, _baseVertex, _indexOffset); + drawInternal(_count, _baseVertex, _instanceCount, _indexOffset); #endif } #endif @@ -832,10 +878,12 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { #endif #endif - #ifndef MAGNUM_TARGET_GLES2 - void drawInternal(Int count, Int baseVertex, GLintptr indexOffset, Int indexStart, Int indexEnd); + #ifndef MAGNUM_TARGET_GLES + void drawInternal(Int count, Int baseVertex, Int instanceCount, UnsignedInt baseInstance, GLintptr indexOffset, Int indexStart, Int indexEnd); + #elif !defined(MAGNUM_TARGET_GLES2) + void drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset, Int indexStart, Int indexEnd); #else - void drawInternal(Int count, Int baseVertex, GLintptr indexOffset); + void drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset); #endif void MAGNUM_LOCAL createImplementationDefault(); @@ -873,9 +921,22 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { void MAGNUM_LOCAL unbindImplementationDefault(); void MAGNUM_LOCAL unbindImplementationVAO(); + #ifdef MAGNUM_TARGET_GLES2 + void MAGNUM_LOCAL drawArraysInstancedImplementationANGLE(GLint baseVertex, GLsizei count, GLsizei instanceCount); + void MAGNUM_LOCAL drawArraysInstancedImplementationEXT(GLint baseVertex, GLsizei count, GLsizei instanceCount); + void MAGNUM_LOCAL drawArraysInstancedImplementationNV(GLint baseVertex, GLsizei count, GLsizei instanceCount); + + void MAGNUM_LOCAL drawElementsInstancedImplementationANGLE(GLsizei count, GLintptr indexOffset, GLsizei instanceCount); + void MAGNUM_LOCAL drawElementsInstancedImplementationEXT(GLsizei count, GLintptr indexOffset, GLsizei instanceCount); + void MAGNUM_LOCAL drawElementsInstancedImplementationNV(GLsizei count, GLintptr indexOffset, GLsizei instanceCount); + #endif + GLuint _id; MeshPrimitive _primitive; - Int _count, _baseVertex; + Int _count, _baseVertex, _instanceCount; + #ifndef MAGNUM_TARGET_GLES + UnsignedInt _baseInstance; + #endif #ifndef MAGNUM_TARGET_GLES2 UnsignedInt _indexStart, _indexEnd; #endif diff --git a/src/Magnum/MeshView.cpp b/src/Magnum/MeshView.cpp index c935612c1..6db77e719 100644 --- a/src/Magnum/MeshView.cpp +++ b/src/Magnum/MeshView.cpp @@ -37,19 +37,23 @@ MeshView& MeshView::setIndexRange(Int first) { void MeshView::draw(AbstractShaderProgram& shader) { shader.use(); - #ifndef MAGNUM_TARGET_GLES2 - _original->drawInternal(_count, _baseVertex, _indexOffset, _indexStart, _indexEnd); + #ifndef MAGNUM_TARGET_GLES + _original->drawInternal(_count, _baseVertex, _instanceCount, _baseInstance, _indexOffset, _indexStart, _indexEnd); + #elif !defined(MAGNUM_TARGET_GLES2) + _original->drawInternal(_count, _baseVertex, _instanceCount, _indexOffset, _indexStart, _indexEnd); #else - _original->drawInternal(_count, _baseVertex, _indexOffset); + _original->drawInternal(_count, _baseVertex, _instanceCount, _indexOffset); #endif } #ifdef MAGNUM_BUILD_DEPRECATED void MeshView::draw() { - #ifndef MAGNUM_TARGET_GLES2 - _original->drawInternal(_count, _baseVertex, _indexOffset, _indexStart, _indexEnd); + #ifndef MAGNUM_TARGET_GLES + _original->drawInternal(_count, _baseVertex, _instanceCount, _baseInstance, _indexOffset, _indexStart, _indexEnd); + #elif !defined(MAGNUM_TARGET_GLES2) + _original->drawInternal(_count, _baseVertex, _instanceCount, _indexOffset, _indexStart, _indexEnd); #else - _original->drawInternal(_count, _baseVertex, _indexOffset); + _original->drawInternal(_count, _baseVertex, _instanceCount, _indexOffset); #endif } #endif diff --git a/src/Magnum/MeshView.h b/src/Magnum/MeshView.h index 31c0b6ca4..94d5ac7f2 100644 --- a/src/Magnum/MeshView.h +++ b/src/Magnum/MeshView.h @@ -172,6 +172,42 @@ class MAGNUM_EXPORT MeshView { } #endif + /** @brief Instance count */ + Int instanceCount() const { return _instanceCount; } + + /** + * @brief Set instance count + * @return Reference to self (for method chaining) + * + * Default is `1`. + * @requires_gl31 %Extension @extension{ARB,draw_instanced} + * @requires_gles30 %Extension @es_extension{ANGLE,instanced_arrays}, + * @es_extension2{EXT,draw_instanced,draw_instanced} or + * @es_extension{NV,draw_instanced} in OpenGL ES 2.0. + */ + MeshView& setInstanceCount(Int count) { + _instanceCount = count; + return *this; + } + + #ifndef MAGNUM_TARGET_GLES + /** @brief Base instance */ + UnsignedInt baseInstance() const { return _baseInstance; } + + /** + * @brief Set base instance + * @return Reference to self (for method chaining) + * + * Default is `0`. + * @requires_gl42 %Extension @extension{ARB,base_instance} + * @requires_gl Base instance cannot be specified in OpenGL ES. + */ + MeshView& setBaseInstance(UnsignedInt baseInstance) { + _baseInstance = baseInstance; + return *this; + } + #endif + /** * @brief Draw the mesh * @@ -192,14 +228,21 @@ class MAGNUM_EXPORT MeshView { private: Mesh* _original; - Int _count, _baseVertex; + Int _count, _baseVertex, _instanceCount; + #ifndef MAGNUM_TARGET_GLES + UnsignedInt _baseInstance; + #endif GLintptr _indexOffset; #ifndef MAGNUM_TARGET_GLES2 UnsignedInt _indexStart, _indexEnd; #endif }; -inline MeshView::MeshView(Mesh& original): _original(&original), _count(0), _baseVertex(0), _indexOffset(0) +inline MeshView::MeshView(Mesh& original): _original(&original), _count(0), _baseVertex(0), _instanceCount{1}, + #ifndef MAGNUM_TARGET_GLES + _baseInstance{0}, + #endif + _indexOffset(0) #ifndef MAGNUM_TARGET_GLES2 , _indexStart(0), _indexEnd(0) #endif @@ -217,7 +260,6 @@ inline MeshView& MeshView::setIndexRange(Int first, UnsignedInt start, UnsignedI return *this; } - } #endif diff --git a/src/Magnum/Test/MeshGLTest.cpp b/src/Magnum/Test/MeshGLTest.cpp index d3da77b2c..28f9bde6b 100644 --- a/src/Magnum/Test/MeshGLTest.cpp +++ b/src/Magnum/Test/MeshGLTest.cpp @@ -110,6 +110,14 @@ class MeshGLTest: public AbstractOpenGLTester { #ifndef MAGNUM_TARGET_GLES void setBaseVertex(); #endif + void setInstanceCount(); + void setInstanceCountIndexed(); + #ifndef MAGNUM_TARGET_GLES + void setInstanceCountBaseInstance(); + void setInstanceCountBaseInstanceIndexed(); + void setInstanceCountBaseVertex(); + void setInstanceCountBaseVertexBaseInstance(); + #endif }; MeshGLTest::MeshGLTest() { @@ -175,7 +183,15 @@ MeshGLTest::MeshGLTest() { &MeshGLTest::setIndexBufferUnsignedInt, #ifndef MAGNUM_TARGET_GLES - &MeshGLTest::setBaseVertex + &MeshGLTest::setBaseVertex, + #endif + &MeshGLTest::setInstanceCount, + &MeshGLTest::setInstanceCountIndexed, + #ifndef MAGNUM_TARGET_GLES + &MeshGLTest::setInstanceCountBaseInstance, + &MeshGLTest::setInstanceCountBaseInstanceIndexed, + &MeshGLTest::setInstanceCountBaseVertex, + &MeshGLTest::setInstanceCountBaseVertexBaseInstance #endif }); } @@ -409,6 +425,10 @@ Checker::Checker(AbstractShaderProgram&& shader, RenderbufferFormat format, Mesh MeshView(mesh) .setCount(1) .setBaseVertex(1) + .setInstanceCount(mesh.instanceCount()) + #ifndef MAGNUM_TARGET_GLES + .setBaseInstance(mesh.baseInstance()) + #endif .draw(shader); } @@ -1195,6 +1215,10 @@ IndexChecker::IndexChecker(Mesh& mesh): framebuffer({{}, Vector2i(1)}) { MeshView(mesh) .setCount(1) .setBaseVertex(mesh.baseVertex()) + .setInstanceCount(mesh.instanceCount()) + #ifndef MAGNUM_TARGET_GLES + .setBaseInstance(mesh.baseInstance()) + #endif .setIndexRange(1) .draw(MultipleShader{}); } @@ -1301,6 +1325,219 @@ void MeshGLTest::setBaseVertex() { } #endif +void MeshGLTest::setInstanceCount() { + /* Verbatim copy of addVertexBufferFloat() with added extension check and + setInstanceCount() call. It would just render three times the same + value. I'm too lazy to invent proper test case, so I'll just check that + it didn't generate any error and rendered something */ + + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available.")); + #elif defined(MAGNUM_TARGET_GLES2) + if(!Context::current()->isExtensionSupported() && !Context::current()->isExtensionSupported() && !Context::current()->isExtensionSupported()) + CORRADE_SKIP("Required extension is not available."); + #endif + + typedef AbstractShaderProgram::Attribute<0, Float> Attribute; + + const Float data[] = { 0.0f, -0.7f, Math::normalize(96) }; + Buffer buffer; + buffer.setData(data, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setInstanceCount(3) + .addVertexBuffer(buffer, 4, Attribute()); + + MAGNUM_VERIFY_NO_ERROR(); + + const auto value = Checker(FloatShader("float", "vec4(valueInterpolated, 0.0, 0.0, 0.0)"), + #ifndef MAGNUM_TARGET_GLES2 + RenderbufferFormat::RGBA8, + #else + RenderbufferFormat::RGBA4, + #endif + mesh).get(ColorFormat::RGBA, ColorType::UnsignedByte); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(value, 96); +} + +void MeshGLTest::setInstanceCountIndexed() { + /* Verbatim copy of setIndexBuffer() with added extension check and + setInstanceCount() call. It would just render three times the same + value. I'm too lazy to invent proper test case, so I'll just check that + it didn't generate any error and rendered something */ + + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available.")); + #elif defined(MAGNUM_TARGET_GLES2) + if(!Context::current()->isExtensionSupported() && !Context::current()->isExtensionSupported() && !Context::current()->isExtensionSupported()) + CORRADE_SKIP("Required extension is not available."); + #endif + + Buffer vertices; + vertices.setData(indexedVertexData, BufferUsage::StaticDraw); + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices(Buffer::Target::ElementArray); + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setInstanceCount(3) + .addVertexBuffer(vertices, 1*4, MultipleShader::Position(), + MultipleShader::Normal(), MultipleShader::TextureCoordinates()) + .setIndexBuffer(indices, 2, Mesh::IndexType::UnsignedShort); + + MAGNUM_VERIFY_NO_ERROR(); + + const auto value = IndexChecker(mesh).get(); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(value, indexedResult); +} + +#ifndef MAGNUM_TARGET_GLES +void MeshGLTest::setInstanceCountBaseInstance() { + /* Verbatim copy of setInstanceCount() with additional extension check and + setBaseInstance() call. It would just render three times the same + value. I'm too lazy to invent proper test case, so I'll just check that + it didn't generate any error and rendered something */ + + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available.")); + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::base_instance::string() + std::string(" is not available.")); + + typedef AbstractShaderProgram::Attribute<0, Float> Attribute; + + const Float data[] = { 0.0f, -0.7f, Math::normalize(96) }; + Buffer buffer; + buffer.setData(data, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setInstanceCount(3) + .setBaseInstance(72) + .addVertexBuffer(buffer, 4, Attribute()); + + MAGNUM_VERIFY_NO_ERROR(); + + const auto value = Checker(FloatShader("float", "vec4(valueInterpolated, 0.0, 0.0, 0.0)"), + #ifndef MAGNUM_TARGET_GLES2 + RenderbufferFormat::RGBA8, + #else + RenderbufferFormat::RGBA4, + #endif + mesh).get(ColorFormat::RGBA, ColorType::UnsignedByte); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(value, 96); +} + +void MeshGLTest::setInstanceCountBaseInstanceIndexed() { + /* Verbatim copy of setInstanceCountIndexed() with additional extension + check and setBaseInstance() call. It would just render three times the + same value. I'm too lazy to invent proper test case, so I'll just check + that it didn't generate any error and rendered something */ + + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available.")); + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::base_instance::string() + std::string(" is not available.")); + + Buffer vertices; + vertices.setData(indexedVertexData, BufferUsage::StaticDraw); + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices(Buffer::Target::ElementArray); + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setInstanceCount(3) + .setBaseInstance(72) + .addVertexBuffer(vertices, 1*4, MultipleShader::Position(), + MultipleShader::Normal(), MultipleShader::TextureCoordinates()) + .setIndexBuffer(indices, 2, Mesh::IndexType::UnsignedShort); + + MAGNUM_VERIFY_NO_ERROR(); + + const auto value = IndexChecker(mesh).get(); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(value, indexedResult); +} + +void MeshGLTest::setInstanceCountBaseVertex() { + /* Verbatim copy of setBaseVertex() with additional extension check and + setInstanceCount() call. It would just render three times the same + value. I'm too lazy to invent proper test case, so I'll just check + that it didn't generate any error and rendered something */ + + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available.")); + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_elements_base_vertex::string() + std::string(" is not available.")); + + Buffer vertices; + vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw); + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices(Buffer::Target::ElementArray); + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setBaseVertex(2) + .setInstanceCount(3) + .addVertexBuffer(vertices, 2*4, MultipleShader::Position(), + MultipleShader::Normal(), MultipleShader::TextureCoordinates()) + .setIndexBuffer(indices, 2, Mesh::IndexType::UnsignedShort); + + MAGNUM_VERIFY_NO_ERROR(); + + const auto value = IndexChecker(mesh).get(); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(value, indexedResult); +} + +void MeshGLTest::setInstanceCountBaseVertexBaseInstance() { + /* Verbatim copy of setInstanceCountBaseVertex() with added extension check + and setBaseInstance() call. It would just render three times the same + value. I'm too lazy to invent proper test case, so I'll just check + that it didn't generate any error and rendered something */ + + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available.")); + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::draw_elements_base_vertex::string() + std::string(" is not available.")); + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::base_instance::string() + std::string(" is not available.")); + + Buffer vertices; + vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw); + + constexpr UnsignedShort indexData[] = { 2, 1, 0 }; + Buffer indices(Buffer::Target::ElementArray); + indices.setData(indexData, BufferUsage::StaticDraw); + + Mesh mesh; + mesh.setBaseVertex(2) + .setInstanceCount(3) + .setBaseInstance(72) + .addVertexBuffer(vertices, 2*4, MultipleShader::Position(), + MultipleShader::Normal(), MultipleShader::TextureCoordinates()) + .setIndexBuffer(indices, 2, Mesh::IndexType::UnsignedShort); + + MAGNUM_VERIFY_NO_ERROR(); + + const auto value = IndexChecker(mesh).get(); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(value, indexedResult); +} +#endif + }} CORRADE_TEST_MAIN(Magnum::Test::MeshGLTest)