Browse Source

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 :)
pull/54/head
Vladimír Vondruš 12 years ago
parent
commit
20df20de0f
  1. 3
      doc/opengl-mapping.dox
  2. 9
      doc/opengl-support.dox
  3. 3
      src/Magnum/Context.cpp
  4. 5
      src/Magnum/Extensions.h
  5. 26
      src/Magnum/Implementation/MeshState.cpp
  6. 5
      src/Magnum/Implementation/MeshState.h
  7. 3
      src/Magnum/Implementation/setupDriverWorkarounds.cpp
  8. 201
      src/Magnum/Mesh.cpp
  9. 103
      src/Magnum/Mesh.h
  10. 16
      src/Magnum/MeshView.cpp
  11. 48
      src/Magnum/MeshView.h
  12. 239
      src/Magnum/Test/MeshGLTest.cpp

3
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()

9
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)

3
src/Magnum/Context.cpp

@ -212,6 +212,7 @@ const std::vector<Extension>& Extension::extensions(Version version) {
static const std::vector<Extension> 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>& 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),

5
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

26
src/Magnum/Implementation/MeshState.cpp

@ -92,6 +92,32 @@ MeshState::MeshState(Context& context, std::vector<std::string>& extensions): cu
unbindImplementation = &Mesh::unbindImplementationDefault;
}
#endif
#ifdef MAGNUM_TARGET_GLES2
/* Instanced draw ímplementation on ES2 */
if(context.isExtensionSupported<Extensions::GL::ANGLE::instanced_arrays>()) {
extensions.push_back(Extensions::GL::ANGLE::instanced_arrays::string());
drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationANGLE;
drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationANGLE;
} else if(context.isExtensionSupported<Extensions::GL::EXT::draw_instanced>()) {
extensions.push_back(Extensions::GL::EXT::draw_instanced::string());
drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationEXT;
drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationEXT;
} else if(context.isExtensionSupported<Extensions::GL::NV::draw_instanced>()) {
extensions.push_back(Extensions::GL::NV::draw_instanced::string());
drawArraysInstancedImplementation = &Mesh::drawArraysInstancedImplementationNV;
drawElementsInstancedImplementation = &Mesh::drawElementsInstancedImplementationNV;
} else {
drawArraysInstancedImplementation = nullptr;
drawElementsInstancedImplementation = nullptr;
}
#endif
}
}}

5
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;

3
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);

201
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<GLvoid*>(indexOffset), baseVertex);
/* Indexed mesh without specified range */
} else glDrawElementsBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(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<GLvoid*>(indexOffset), baseVertex);
/* Indexed mesh */
} else glDrawElementsBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(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<GLvoid*>(indexOffset));
/* Indexed mesh */
} else
#endif
{
glDrawElements(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(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<GLvoid*>(indexOffset));
/* Indexed mesh without specified range */
} else
#endif
{
glDrawElements(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(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<GLvoid*>(indexOffset), instanceCount, baseVertex, baseInstance);
/* Indexed mesh with base vertex */
else
glDrawElementsInstancedBaseVertex(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(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<GLvoid*>(indexOffset), instanceCount, baseInstance);
/* Instanced mesh */
} else
#endif
{
#ifndef MAGNUM_TARGET_GLES2
glDrawElementsInstanced(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(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<void>(baseVertex);
static_cast<void>(count);
static_cast<void>(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<void>(baseVertex);
static_cast<void>(count);
static_cast<void>(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<void>(baseVertex);
static_cast<void>(count);
static_cast<void>(instanceCount);
}
void Mesh::drawElementsInstancedImplementationANGLE(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) {
//glDrawElementsInstancedANGLE(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(indexOffset), instanceCount);
CORRADE_INTERNAL_ASSERT(false);
static_cast<void>(count);
static_cast<void>(indexOffset);
static_cast<void>(instanceCount);
}
void Mesh::drawElementsInstancedImplementationEXT(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) {
//glDrawElementsInstancedEXT(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(indexOffset), instanceCount);
CORRADE_INTERNAL_ASSERT(false);
static_cast<void>(count);
static_cast<void>(indexOffset);
static_cast<void>(instanceCount);
}
void Mesh::drawElementsInstancedImplementationNV(const GLsizei count, const GLintptr indexOffset, const GLsizei instanceCount) {
//glDrawElementsInstancedNV(GLenum(_primitive), count, GLenum(_indexType), reinterpret_cast<GLvoid*>(indexOffset), instanceCount);
CORRADE_INTERNAL_ASSERT(false);
static_cast<void>(count);
static_cast<void>(indexOffset);
static_cast<void>(instanceCount);
}
#endif
#ifndef DOXYGEN_GENERATING_OUTPUT
Debug operator<<(Debug debug, MeshPrimitive value) {
switch(value) {

103
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

16
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

48
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

239
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<Extensions::GL::ARB::draw_instanced>())
CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available."));
#elif defined(MAGNUM_TARGET_GLES2)
if(!Context::current()->isExtensionSupported<Extensions::GL::ANGLE::instanced_arrays>() && !Context::current()->isExtensionSupported<Extensions::GL::EXT::draw_instanced>() && !Context::current()->isExtensionSupported<Extensions::GL::NV::draw_instanced>())
CORRADE_SKIP("Required extension is not available.");
#endif
typedef AbstractShaderProgram::Attribute<0, Float> Attribute;
const Float data[] = { 0.0f, -0.7f, Math::normalize<Float, UnsignedByte>(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<UnsignedByte>(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<Extensions::GL::ARB::draw_instanced>())
CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available."));
#elif defined(MAGNUM_TARGET_GLES2)
if(!Context::current()->isExtensionSupported<Extensions::GL::ANGLE::instanced_arrays>() && !Context::current()->isExtensionSupported<Extensions::GL::EXT::draw_instanced>() && !Context::current()->isExtensionSupported<Extensions::GL::NV::draw_instanced>())
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<Extensions::GL::ARB::draw_instanced>())
CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available."));
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::base_instance>())
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<Float, UnsignedByte>(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<UnsignedByte>(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<Extensions::GL::ARB::draw_instanced>())
CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available."));
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::base_instance>())
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<Extensions::GL::ARB::draw_instanced>())
CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available."));
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::draw_elements_base_vertex>())
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<Extensions::GL::ARB::draw_instanced>())
CORRADE_SKIP(Extensions::GL::ARB::draw_instanced::string() + std::string(" is not available."));
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::GL::ARB::draw_elements_base_vertex::string() + std::string(" is not available."));
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::base_instance>())
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)

Loading…
Cancel
Save