Browse Source

GL: implement ANGLE- and WebGL-specific instanced multidraw.

Yes, this is only ever provided by an ANGLE extension, and as you might
have guessed, The Great Google Overlords didn't bother caring to have
this supported outside of their browser.
pull/240/head
Vladimír Vondruš 5 years ago
parent
commit
b30d313ecd
  1. 6
      doc/changelog.dox
  2. 4
      doc/opengl-support.dox
  3. 60
      src/Magnum/GL/AbstractShaderProgram.cpp
  4. 190
      src/Magnum/GL/AbstractShaderProgram.h
  5. 74
      src/Magnum/GL/Implementation/MeshState.cpp
  6. 8
      src/Magnum/GL/Implementation/MeshState.h
  7. 242
      src/Magnum/GL/Mesh.cpp
  8. 30
      src/Magnum/GL/Mesh.h
  9. 892
      src/Magnum/GL/Test/MeshGLTest.cpp

6
doc/changelog.dox

@ -64,6 +64,12 @@ See also:
- New @ref GL::AbstractShaderProgram::draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
overload for data-oriented multi-draw workflows without @ref GL::MeshView
and internal temporary allocations
- Implemented ANGLE- and WebGL-specific instanced multidraw with
@ref GL::AbstractShaderProgram::draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&).
It's added mainly in order to provide a fast rendering path on
resource-constrainted systems and as such doesn't have an overload taking
@ref GL::MeshView instances or a fallback path when the multidraw
extensions are not available.
- New @ref GL::Context::Configuration class providing runtime alternatives to
the `--magnum-log`, `--magnum-gpu-validation`, `--magnum-disable-extensions`
and `--magnum-disable-workarounds` command line options. The class is then

4
doc/opengl-support.dox

@ -450,7 +450,7 @@ Extension | Status
@gl_extension2{ANGLE,texture_compression_dxt3,ANGLE_texture_compression_dxt} | done
@gl_extension2{ANGLE,texture_compression_dxt5,ANGLE_texture_compression_dxt} | done
@m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted) | done
@m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) (unlisted) | done except instanced multi-draw
@m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_base_vertex_base_instance.txt) (unlisted) | done
@gl_extension{APPLE,texture_format_BGRA8888} | done
@gl_extension{APPLE,clip_distance} | done
@gl_extension{ARM,shader_framebuffer_fetch} | missing renderer setup and limit query
@ -574,7 +574,7 @@ Extension | Status
@webgl_extension{WEBGL,multi_draw} | done
@webgl_extension{WEBGL,blend_equation_advanced_coherent} | done
@webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance} | done
@webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} | done except instanced multi-draw
@webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance} | done
@section opengl-unsupported Unsupported OpenGL features

60
src/Magnum/GL/AbstractShaderProgram.cpp

@ -407,6 +407,66 @@ AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers:
}
#endif
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_GLES2
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets) {
if(!counts.size()) return *this;
use();
mesh.drawInternalStrided(counts, instanceCounts, vertexOffsets, indexOffsets, instanceOffsets);
return *this;
}
#ifndef CORRADE_TARGET_32BIT
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets) {
if(!counts.size()) return *this;
use();
mesh.drawInternalStrided(counts, instanceCounts, vertexOffsets, indexOffsets, instanceOffsets);
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets) {
return draw(mesh, counts, instanceCounts, vertexOffsets, Containers::StridedArrayView1D<const UnsignedLong>{}, instanceOffsets);
}
#endif
#endif
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
if(!counts.size()) return *this;
use();
mesh.drawInternalStrided(counts, instanceCounts, vertexOffsets, indexOffsets
#ifndef MAGNUM_TARGET_GLES2
, Containers::StridedArrayView1D<const UnsignedInt>{}
#endif
);
return *this;
}
#ifndef CORRADE_TARGET_32BIT
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
if(!counts.size()) return *this;
use();
mesh.drawInternalStrided(counts, instanceCounts, vertexOffsets, indexOffsets
#ifndef MAGNUM_TARGET_GLES2
, Containers::StridedArrayView1D<const UnsignedInt>{}
#endif
);
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return draw(mesh, counts, instanceCounts, vertexOffsets, Containers::StridedArrayView1D<const UnsignedLong>{});
}
#endif
#endif
AbstractShaderProgram& AbstractShaderProgram::draw(Containers::ArrayView<const Containers::Reference<MeshView>> meshes) {
if(meshes.empty()) return *this;

190
src/Magnum/GL/AbstractShaderProgram.h

@ -922,6 +922,188 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t);
#endif
#if defined(MAGNUM_TARGET_GLES) || defined(DOXYGEN_GENERATING_OUTPUT)
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Draw multiple instanced mesh views with instance offsets at once
* @param mesh The mesh from which to draw
* @param counts Vertex/index counts for each draw
* @param instanceCounts Instance counts for each draw. Expected to
* have the same size as @p counts.
* @param vertexOffsets Offsets into the vertex array for non-indexed
* meshes, base vertex for indexed meshes. Expected to have the
* same size as @p counts, for indexed meshes it can be also empty
* in which case the base vertex is assumed to be @cpp 0 @ce for
* all draws.
* @param indexOffsets Offsets into the index buffer for indexed
* meshes, *in bytes*. Expected to have the same size as @p counts
* for indexed meshes, ignored for non-indexed.
* @param instanceOffsets Offsets to be added to the instance index for
* each draw. Expected to either be empty or have the same size as
* @p counts.
* @return Reference to self (for method chaining)
* @m_since_latest
*
* If @p counts, @p instanceCounts, @p vertexOffsets, @p indexOffsets
* and @p instanceOffsets are contiguous views, they get passed
* directly to the underlying GL functions, otherwise a temporary
* contiguous copy is allocated. There are the following special cases,
* however:
*
* - On @ref CORRADE_TARGET_32BIT "64-bit builds" the
* @p indexOffsets additionally have to be 64-bit in order to
* avoid a copy because the @m_class{m-doc-external} [glMultiDrawArraysInstancedANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_multi_draw.txt)
* / @m_class{m-doc-external} [glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* functions accept them as pointers, see the @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedLong>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
* overload below.
* - If the @p mesh is indexed, @p vertexOffsets and
* @p instanceOffsets have to be either both specified or both
* empty in order to avoid a copy with the missing one filled with
* zeros, as there's only either @m_class{m-doc-external} [glMultiDrawElementsInstancedANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_multi_draw.txt)
* or @m_class{m-doc-external} [glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt),
* but no function accepting just one of them.
*
* @see @ref draw(MeshView&), @fn_gl{UseProgram},
* @fn_gl_keyword{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl_keyword{VertexAttribPointer}, @fn_gl_keyword{DisableVertexAttribArray}
* or @fn_gl{BindVertexArray},
* @m_class{m-doc-external} [glMultiDrawArraysInstancedANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_multi_draw.txt)
* / @m_class{m-doc-external} [glMultiDrawArraysInstancedBaseInstanceANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt) or
* @m_class{m-doc-external} [glMultiDrawElementsInstancedANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_multi_draw.txt)
* / @m_class{m-doc-external} [glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* @requires_gles_only Not available on desktop OpenGL.
* @requires_gles30 Not defined in OpenGL ES 2.0. Use
* @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&) without the
* @p instanceOffsets parameter there instead.
* @requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* @requires_es_extension OpenGL ES 3.1 and extension
* @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* if the mesh is indexed and the @p vertexOffsets view is not
* empty
* @requires_es_extension OpenGL ES 3.1 and extension
* @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* if the @p instanceOffsets view is not empty
* @requires_webgl_extension Extension @webgl_extension{WEBGL,multi_draw}.
* Note that this extension is only implemented since Emscripten
* 2.0.0 and thus it's not even advertised on older versions.
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
* if the mesh is indexed and the @p vertexOffsets view is not
* empty.
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
* if the @p instanceOffsets view is not empty.
* @m_keywords{glMultiDrawArraysInstancedANGLE() glMultiDrawArraysInstancedBaseInstanceANGLE() glMultiDrawElementsInstancedANGLE() glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE()}
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets);
#ifndef CORRADE_TARGET_32BIT
/**
* @brief Draw multiple instanced mesh views with instance offsets at once
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Defined only on @ref CORRADE_TARGET_32BIT "64-bit builds". Compared
* to @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
* this overload can avoid allocating an array of 64-bit pointers for
* the @m_class{m-doc-external} [glMultiDrawElementsInstancedANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_multi_draw.txt)
* / @m_class{m-doc-external} [glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* function and can instead directly reuse the @p indexOffsets view if
* it's contiguous. See the original overload for further information.
* @requires_gles_only Not available on desktop OpenGL.
* @requires_gles30 Not defined in OpenGL ES 2.0. Use
* @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&) without the
* @p instanceOffsets parameter there instead.
* @requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* @requires_es_extension OpenGL ES 3.1 and extension
* @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* if the mesh is indexed and the @p vertexOffsets view is not
* empty
* @requires_es_extension OpenGL ES 3.1 and extension
* @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* if the @p instanceOffsets view is not empty
* @requires_webgl_extension Extension @webgl_extension{WEBGL,multi_draw}
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
* if the mesh is indexed and the @p vertexOffsets view is not
* empty.
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
* if the @p instanceOffsets view is not empty.
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets);
/**
* @overload
* @m_since_latest
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets);
#endif
#endif
/**
* @brief Draw multiple instanced mesh views at once
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Compared to @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
* lacks the last @p instanceOffsets parameter and as such is available
* also in OpenGL ES 2.0 and WebGL 1.0.
*
* If OpenGL ES 3.0, WebGL 2.0, @gl_extension{OES,vertex_array_object}
* in OpenGL ES 2.0 or @webgl_extension{OES,vertex_array_object} in
* WebGL 1.0 is available, the associated vertex array object is bound
* instead of setting up the mesh from scratch.
*
* @requires_gles_only Not available on desktop OpenGL.
* @requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* @requires_es_extension OpenGL ES 3.1 and extension
* @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* if the mesh is indexed and the @p vertexOffsets view is not
* empty
* @requires_webgl_extension Extension @webgl_extension{WEBGL,multi_draw}
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
* if the mesh is indexed and the @p vertexOffsets view is not
* empty.
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets);
#ifndef CORRADE_TARGET_32BIT
/**
* @brief Draw multiple instanced mesh views at once
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Defined only on @ref CORRADE_TARGET_32BIT "64-bit builds". Compared
* to @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
* this overload can avoid allocating an array of 64-bit pointers for
* the @m_class{m-doc-external} [glMultiDrawElementsInstancedANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_multi_draw.txt)
* / @m_class{m-doc-external} [glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE()](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* function and can instead directly reuse the @p indexOffsets view if
* it's contiguous. See the original overload for further information.
* @requires_gles_only Not available on desktop OpenGL.
* @requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* @requires_es_extension OpenGL ES 3.1 and extension
* @m_class{m-doc-external} [ANGLE_base_vertex_base_instance](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_base_vertex_base_instance.txt)
* if the mesh is indexed and the @p vertexOffsets view is not
* empty
* @requires_webgl_extension Extension @webgl_extension{WEBGL,multi_draw}
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
* if the mesh is indexed and the @p vertexOffsets view is not
* empty.
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets);
/**
* @overload
* @m_since_latest
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t);
#endif
#endif
/**
* @brief Draw multiple mesh views at once
* @return Reference to self (for method chaining)
@ -941,7 +1123,13 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
* Emscripten 2.0.0, so it's not even advertised on older versions.
*
* @attention All meshes must be views of the same original mesh and
* must not be instanced.
* must not be instanced. Instanced multidraw is available only
* through @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
* which relies on GLES- and WebGL-specific extensions. It exists
* mainly in order to provide a fast rendering path on
* resource-constrainted systems and as such doesn't have an
* overload taking @ref MeshView instances or a fallback path when
* the multidraw extensions are not available.
*
* @requires_gl32 Extension @gl_extension{ARB,draw_elements_base_vertex}
* if the mesh is indexed and @ref MeshView::baseVertex() is not

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

@ -227,6 +227,7 @@ MeshState::MeshState(Context& context, ContextState& contextState, Containers::S
if(context.isExtensionSupported<Extensions::WEBGL::multi_draw>())
#endif
{
/* General multi-draw extension */
#ifndef MAGNUM_TARGET_WEBGL
if(context.isExtensionSupported<Extensions::EXT::multi_draw_arrays>()) {
extensions[Extensions::EXT::multi_draw_arrays::Index] =
@ -260,8 +261,9 @@ MeshState::MeshState(Context& context, ContextState& contextState, Containers::S
}
#endif
/* These function pointers make sense only if the general multi-draw
extension is supported. Also, not on WebGL 1 at all. */
/* Base vertex specification. These function pointers make sense only
if the general multi-draw extension is supported. Also, not on WebGL
1 at all. */
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
#ifndef MAGNUM_TARGET_WEBGL
if(context.isExtensionSupported<Extensions::EXT::draw_elements_base_vertex>()) {
@ -301,6 +303,74 @@ MeshState::MeshState(Context& context, ContextState& contextState, Containers::S
}
#endif
/* Instanced multi-draw. ES- and WebGL-only. */
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_WEBGL
if(context.isExtensionSupported<Extensions::ANGLE::multi_draw>()) {
extensions[Extensions::ANGLE::multi_draw::Index] =
Extensions::ANGLE::multi_draw::string();
multiDrawArraysInstancedImplementation = glMultiDrawArraysInstancedANGLE;
multiDrawElementsInstancedImplementation = glMultiDrawElementsInstancedANGLE;
} else {
multiDrawArraysInstancedImplementation = nullptr;
multiDrawElementsInstancedImplementation = nullptr;
}
#else
{
extensions[Extensions::WEBGL::multi_draw::Index] =
Extensions::WEBGL::multi_draw::string();
/* The WEBGL extension uses the same entrypoints as the ANGLE
extension it was based on. Only available since 2.0.0:
https://github.com/emscripten-core/emscripten/pull/11650 */
#if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 20000
multiDrawArraysInstancedImplementation = glMultiDrawArraysInstancedANGLE;
multiDrawElementsInstancedImplementation = glMultiDrawElementsInstancedANGLE;
#else
/* In Context::setupDriverWorkarounds() we make sure the extension
is not even advertised, so this shouldn't be reached. */
CORRADE_INTERNAL_ASSERT_UNREACHABLE();
#endif
}
#endif
#endif
/* Instanced multi-draw with base vertex / base instance. ES 3.1 and
WebGL 2 only. */
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2)
#ifndef MAGNUM_TARGET_WEBGL
if(context.isExtensionSupported<Extensions::ANGLE::base_vertex_base_instance>()) {
extensions[Extensions::ANGLE::base_vertex_base_instance::Index] =
Extensions::ANGLE::base_vertex_base_instance::string();
multiDrawArraysInstancedBaseInstanceImplementation = glMultiDrawArraysInstancedBaseInstanceANGLE;
multiDrawElementsInstancedBaseVertexBaseInstanceImplementation = glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE;
} else
#else
if(context.isExtensionSupported<Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>()) {
extensions[Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::Index] =
Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string();
/* The WEBGL extension uses the same entrypoints as the ANGLE
extension it was based on. Only available since 2.0.0:
https://github.com/emscripten-core/emscripten/pull/11650 */
#if __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 20000
multiDrawArraysInstancedBaseInstanceImplementation = glMultiDrawArraysInstancedBaseInstanceANGLE;
multiDrawElementsInstancedBaseVertexBaseInstanceImplementation = glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE;
#else
/* In Context::setupDriverWorkarounds() we make sure the extension
is not even advertised, so this shouldn't be reached. */
CORRADE_INTERNAL_ASSERT_UNREACHABLE();
#endif
} else
#endif
{
multiDrawArraysInstancedBaseInstanceImplementation = Mesh::multiDrawArraysInstancedBaseInstanceImplementationAssert;
multiDrawElementsInstancedBaseVertexBaseInstanceImplementation = Mesh::multiDrawElementsInstancedBaseVertexBaseInstanceImplementationAssert;
}
#endif
multiDrawViewImplementation = &MeshView::multiDrawImplementationDefault;
} else {

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

@ -83,6 +83,14 @@ struct MeshState {
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
void(APIENTRY *multiDrawElementsBaseVertexImplementation)(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei, const GLint*);
#endif
#ifdef MAGNUM_TARGET_GLES
void(APIENTRY *multiDrawArraysInstancedImplementation)(GLenum, const GLint*, const GLsizei*, const GLsizei*, GLsizei);
void(APIENTRY *multiDrawElementsInstancedImplementation)(GLenum, const GLint*, GLenum, const void* const*, const GLsizei*, GLsizei);
#ifndef MAGNUM_TARGET_GLES2
void(APIENTRY *multiDrawArraysInstancedBaseInstanceImplementation)(GLenum, const GLint*, const GLsizei*, const GLsizei*, const GLuint*, GLsizei);
void(APIENTRY *multiDrawElementsInstancedBaseVertexBaseInstanceImplementation)(GLenum, const GLint*, GLenum, const void* const*, const GLsizei*, const GLint*, const GLuint*, GLsizei);
#endif
#endif
#endif
void(*bindVAOImplementation)(GLuint);

242
src/Magnum/GL/Mesh.cpp

@ -464,6 +464,83 @@ void Mesh::drawInternal(const Containers::ArrayView<const UnsignedInt>& counts,
(this->*state.unbindImplementation)();
}
#ifdef MAGNUM_TARGET_GLES
void Mesh::drawInternal(const Containers::ArrayView<const UnsignedInt>& counts, const Containers::ArrayView<const UnsignedInt>& instanceCounts, const Containers::ArrayView<const UnsignedInt>& vertexOffsets,
#ifdef CORRADE_TARGET_32BIT
const Containers::ArrayView<const UnsignedInt>& indexOffsets
#else
const Containers::ArrayView<const UnsignedLong>& indexOffsets
#endif
#ifndef MAGNUM_TARGET_GLES2
, const Containers::ArrayView<const UnsignedInt>& instanceOffsets
#endif
) {
const Implementation::MeshState& state = Context::current().state().mesh;
(this->*state.bindImplementation)();
CORRADE_ASSERT(instanceCounts.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "instance count items but got" << instanceCounts.size(), );
/* Non-indexed instanced meshes */
if(!_indexBuffer.id()) {
CORRADE_ASSERT(vertexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "vertex offset items but got" << vertexOffsets.size(), );
/* Non-indexed instanced meshes */
#ifndef MAGNUM_TARGET_GLES2
if(instanceOffsets.empty())
#endif
{
state.multiDrawArraysInstancedImplementation(GLenum(_primitive), reinterpret_cast<const GLint*>(vertexOffsets.data()), reinterpret_cast<const GLsizei*>(counts.data()), reinterpret_cast<const GLsizei*>(instanceCounts.data()), counts.size());
}
/* Non-indexed instanced meshes with base instance */
#ifndef MAGNUM_TARGET_GLES2
else {
CORRADE_ASSERT(instanceOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "instance offset items but got" << instanceOffsets.size(), );
state.multiDrawArraysInstancedBaseInstanceImplementation(GLenum(_primitive), reinterpret_cast<const GLint*>(vertexOffsets.data()), reinterpret_cast<const GLsizei*>(counts.data()), reinterpret_cast<const GLsizei*>(instanceCounts.data()),
reinterpret_cast<const GLuint*>(instanceOffsets.data()), counts.size());
}
#endif
/* Indexed meshes */
} else {
CORRADE_ASSERT(indexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "index offset items but got" << indexOffsets.size(), );
/* Indexed meshes */
if(vertexOffsets.empty()
#ifndef MAGNUM_TARGET_GLES2
&& instanceOffsets.empty()
#endif
) {
state.multiDrawElementsInstancedImplementation(GLenum(_primitive), reinterpret_cast<const GLsizei*>(counts.data()), GLenum(_indexType), reinterpret_cast<const void* const*>(indexOffsets.data()), reinterpret_cast<const GLsizei*>(instanceCounts.data()), counts.size());
/* Indexed meshes with base vertex / base instance. According to the
extension spec both have to be present, not just one. */
} else {
CORRADE_ASSERT(vertexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "vertex offset items but got" << vertexOffsets.size(), );
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(instanceOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "instance offset items but got" << instanceOffsets.size(), );
#endif
#ifndef MAGNUM_TARGET_GLES2
state.multiDrawElementsInstancedBaseVertexBaseInstanceImplementation(GLenum(_primitive), reinterpret_cast<const GLsizei*>(counts.data()), GLenum(_indexType), reinterpret_cast<const void* const*>(indexOffsets.data()), reinterpret_cast<const GLsizei*>(instanceCounts.data()), reinterpret_cast<const GLint*>(vertexOffsets.data()),
reinterpret_cast<const GLuint*>(instanceOffsets.data()), counts.size());
#else
CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): instanced indexed mesh multi-draw with base vertex specification possible only since OpenGL ES 3.0 and WebGL 2.0", );
#endif
}
}
(this->*state.unbindImplementation)();
}
#endif
void Mesh::drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
#ifdef CORRADE_TARGET_32BIT
/* If all views are contiguous and we're on 32-bit, call the implementation
@ -545,6 +622,161 @@ void Mesh::drawInternalStrided(const Containers::StridedArrayView1D<const Unsign
}
#endif
#ifdef MAGNUM_TARGET_GLES
void Mesh::drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets
#ifndef MAGNUM_TARGET_GLES2
, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets
#endif
) {
#ifdef CORRADE_TARGET_32BIT
/* If all views are contiguous, the mesh specifies either both base vertex
and base instance or neither and we're on 32-bit, call the
implementation directly */
if(counts.isContiguous() && instanceCounts.isContiguous() && vertexOffsets.isContiguous() && indexOffsets.isContiguous()
#ifndef MAGNUM_TARGET_GLES2
&& instanceOffsets.isContiguous() && (!_indexBuffer.id() || vertexOffsets.size() == instanceOffsets.size())
#endif
)
return drawInternal(counts.asContiguous(), instanceCounts.asContiguous(), vertexOffsets.asContiguous(), indexOffsets.asContiguous()
#ifndef MAGNUM_TARGET_GLES2
, instanceOffsets.asContiguous()
#endif
);
#endif
/* Expected vertex offset and instance offset count. If the mesh is
indexed, they either have to be both used or both empty. */
std::size_t expectedVertexOffsetCount = vertexOffsets.size();
#ifndef MAGNUM_TARGET_GLES2
std::size_t expectedInstanceOffsetCount = instanceOffsets.size();
if(_indexBuffer.id()) {
/* Use counts.size() instead of <the-other>OffsetCount to avoid hitting
a wrong assert in case the vertex/instance count doesn't match */
if(expectedVertexOffsetCount && !expectedInstanceOffsetCount)
expectedInstanceOffsetCount = counts.size();
else if(expectedInstanceOffsetCount && !expectedVertexOffsetCount)
expectedVertexOffsetCount = counts.size();
}
#endif
/* Otherwise allocate contiguous copies. While it's possible that some
views could have been contigous already and some not, such scenario is
unlikely to make a practical sense, so we'll allocate & copy always. */
Containers::ArrayView<UnsignedInt> countsContiguous;
Containers::ArrayView<UnsignedInt> instanceCountsContiguous;
Containers::ArrayView<UnsignedInt> vertexOffsetsContiguous;
#ifdef CORRADE_TARGET_32BIT
Containers::ArrayView<UnsignedInt>
#else
Containers::ArrayView<UnsignedLong>
#endif
indexOffsetsContiguous;
#ifndef MAGNUM_TARGET_GLES2
Containers::ArrayView<UnsignedInt> instanceOffsetsContiguous;
#endif
Containers::ArrayTuple data{
{NoInit, counts.size(), countsContiguous},
{NoInit, instanceCounts.size(), instanceCountsContiguous},
/* Zero init vertex offsets if we don't have them */
vertexOffsets.size() ?
Containers::ArrayTuple::Item{NoInit, expectedVertexOffsetCount, vertexOffsetsContiguous} :
Containers::ArrayTuple::Item{ValueInit, expectedVertexOffsetCount, vertexOffsetsContiguous},
/* On 64-bit we'll be filling just the lower 32 bits so zero-init the
array. On 32-bit we'll overwrite it completely, so NoInit is fine. */
{
#ifdef CORRADE_TARGET_32BIT
NoInit
#else
ValueInit
#endif
, indexOffsets.size(), indexOffsetsContiguous},
#ifndef MAGNUM_TARGET_GLES2
/* Zero init instance offsets if we don't have them */
instanceOffsets.size() ?
Containers::ArrayTuple::Item{NoInit, expectedInstanceOffsetCount, instanceOffsetsContiguous} :
Containers::ArrayTuple::Item{ValueInit, expectedInstanceOffsetCount, instanceOffsetsContiguous},
#endif
};
Utility::copy(counts, countsContiguous);
Utility::copy(instanceCounts, instanceCountsContiguous);
/* Copy vertex offsets only if we have them and leave them zero-init'd
otherwise */
if(vertexOffsets.size())
Utility::copy(vertexOffsets, vertexOffsetsContiguous);
Utility::copy(indexOffsets,
#ifdef CORRADE_TARGET_32BIT
indexOffsetsContiguous
#else
/* Write to the lower 32 bits of the index offsets, which is the
leftmost bits on Little-Endian and rightmost on Big-Endian. On LE it
could be just Containers::arrayCast<const UnsignedInt>(indexOffsets)
but to minimize a chance of error on BE platforms that are hard to
test on, the same code is used for both. */
Containers::arrayCast<2, UnsignedInt>(stridedArrayView(indexOffsetsContiguous)).transposed<0, 1>()[
#ifndef CORRADE_TARGET_BIG_ENDIAN
0
#else
1
#endif
]
#endif
);
#ifndef MAGNUM_TARGET_GLES2
/* Copy instance offsets only if we have them and leave them zero-init'd
otherwise */
if(instanceOffsets.size())
Utility::copy(instanceOffsets, instanceOffsetsContiguous);
#endif
drawInternal(countsContiguous, instanceCountsContiguous, vertexOffsetsContiguous, indexOffsetsContiguous
#ifndef MAGNUM_TARGET_GLES2
, instanceOffsetsContiguous
#endif
);
}
#ifndef CORRADE_TARGET_32BIT
void Mesh::drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets
#ifndef MAGNUM_TARGET_GLES2
, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets
#endif
) {
/* If all views are contiguous, the mesh specifies either both base vertex
and base instance or neither, call the implementation directly */
if(counts.isContiguous() && instanceCounts.isContiguous() && vertexOffsets.isContiguous() && indexOffsets.isContiguous()
#ifndef MAGNUM_TARGET_GLES2
&& instanceOffsets.isContiguous() && (!_indexBuffer.id() || vertexOffsets.size() == instanceOffsets.size())
#endif
)
return drawInternal(counts.asContiguous(), instanceCounts.asContiguous(), vertexOffsets.asContiguous(), indexOffsets.asContiguous()
#ifndef MAGNUM_TARGET_GLES2
, instanceOffsets.asContiguous()
#endif
);
/* Otherwise delegate into the 32-bit variant, which will allocate a
contiguous copy */
drawInternalStrided(counts, instanceCounts, vertexOffsets,
/* Get the lower 32 bits of the index offsets, which is the leftmost
bits on Little-Endian and rightmost on Big-Endian. On LE it could be
just Containers::arrayCast<const UnsignedInt>(indexOffsets) but to
minimize a chance of error on BE platforms that are hard to test on,
the same code is used for both. */
Containers::arrayCast<2, const UnsignedInt>(indexOffsets).transposed<0, 1>()[
#ifndef CORRADE_TARGET_BIG_ENDIAN
0
#else
1
#endif
]
#ifndef MAGNUM_TARGET_GLES2
, instanceOffsets
#endif
);
}
#endif
#endif
#ifndef MAGNUM_TARGET_GLES2
void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, UnsignedInt baseInstance, GLintptr indexOffset, Int indexStart, Int indexEnd)
#else
@ -1120,6 +1352,16 @@ void Mesh::multiDrawElementsBaseVertexImplementationANGLE(const GLenum mode, con
void Mesh::multiDrawElementsBaseVertexImplementationAssert(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei, const GLint*) {
CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for indexed mesh multi-draw with base vertex specification", );
}
#ifndef MAGNUM_TARGET_GLES2
void Mesh::multiDrawArraysInstancedBaseInstanceImplementationAssert(GLenum, const GLint*, const GLsizei*, const GLsizei*, const GLuint*, GLsizei) {
CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced mesh multi-draw with base instance specification", );
}
void Mesh::multiDrawElementsInstancedBaseVertexBaseInstanceImplementationAssert(GLenum, const GLint*, GLenum, const void* const*, const GLsizei*, const GLint*, const GLuint*, GLsizei) {
CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh multi-draw with base vertex and base instance specification", );
}
#endif
#endif
}}

30
src/Magnum/GL/Mesh.h

@ -1096,10 +1096,36 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
const Containers::ArrayView<const UnsignedLong>& indexOffsets
#endif
);
#ifdef MAGNUM_TARGET_GLES
MAGNUM_GL_LOCAL void drawInternal(const Containers::ArrayView<const UnsignedInt>& counts, const Containers::ArrayView<const UnsignedInt>& instanceCounts, const Containers::ArrayView<const UnsignedInt>& vertexOffsets,
#ifdef CORRADE_TARGET_32BIT
const Containers::ArrayView<const UnsignedInt>& indexOffsets
#else
const Containers::ArrayView<const UnsignedLong>& indexOffsets
#endif
#ifndef MAGNUM_TARGET_GLES2
, const Containers::ArrayView<const UnsignedInt>& instanceOffsets
#endif
);
#endif
MAGNUM_GL_LOCAL void drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets);
#ifndef CORRADE_TARGET_32BIT
MAGNUM_GL_LOCAL void drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets);
#endif
#ifdef MAGNUM_TARGET_GLES
MAGNUM_GL_LOCAL void drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets
#ifndef MAGNUM_TARGET_GLES2
, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets
#endif
);
#ifndef CORRADE_TARGET_32BIT
MAGNUM_GL_LOCAL void drawInternalStrided(const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& instanceCounts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets
#ifndef MAGNUM_TARGET_GLES2
, const Containers::StridedArrayView1D<const UnsignedInt>& instanceOffsets
#endif
);
#endif
#endif
#ifndef MAGNUM_TARGET_GLES
MAGNUM_GL_LOCAL void drawInternal(TransformFeedback& xfb, UnsignedInt stream, Int instanceCount);
@ -1193,6 +1219,10 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
static MAGNUM_GL_LOCAL void multiDrawElementsBaseVertexImplementationANGLE(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawCount, const GLint* baseVertex);
#endif
static MAGNUM_GL_LOCAL void multiDrawElementsBaseVertexImplementationAssert(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei, const GLint*);
#ifndef MAGNUM_TARGET_GLES2
static MAGNUM_GL_LOCAL void multiDrawArraysInstancedBaseInstanceImplementationAssert(GLenum, const GLint*, const GLsizei*, const GLsizei*, const GLuint*, GLsizei);
static MAGNUM_GL_LOCAL void multiDrawElementsInstancedBaseVertexBaseInstanceImplementationAssert(GLenum, const GLint*, GLenum, const void* const*, const GLsizei*, const GLint*, const GLuint*, GLsizei);
#endif
#endif
/* _id, _primitive, _flags set from constructors */

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

@ -198,6 +198,27 @@ struct MeshGLTest: OpenGLTester {
#endif
void multiDrawViewsInstanced();
void multiDrawViewsDifferentMeshes();
#ifdef MAGNUM_TARGET_GLES
void multiDrawInstanced();
void multiDrawInstancedSparseArrays();
/* no MeshView support for instanced multidraw */
template<class T> void multiDrawInstancedIndexed();
template<class T> void multiDrawInstancedIndexedSparseArrays();
/* no MeshView support for instanced multidraw */
void multiDrawInstancedWrongInstanceCountSize();
void multiDrawInstancedWrongVertexOffsetSize();
#ifndef MAGNUM_TARGET_GLES2
void multiDrawInstancedWrongInstanceOffsetSize();
#endif
void multiDrawInstancedIndexedWrongInstanceCountSize();
void multiDrawInstancedIndexedWrongVertexOffsetSize();
void multiDrawInstancedIndexedWrongIndexOffsetSize();
#ifndef MAGNUM_TARGET_GLES2
void multiDrawInstancedIndexedWrongInstanceOffsetSize();
void multiDrawInstancedBaseVertexNoExtensionAvailable();
void multiDrawInstancedBaseInstanceNoExtensionAvailable();
#endif
#endif
};
const struct {
@ -351,6 +372,167 @@ const struct {
{0.25f, 0.5f, 0.0f, 1.0f}}
};
#ifdef MAGNUM_TARGET_GLES
const struct {
const char* name;
bool vertexId;
bool drawId;
Vector3 values[2];
UnsignedInt counts[2];
UnsignedInt instanceCounts[2];
UnsignedInt vertexOffsets[2];
UnsignedInt instanceOffsets[2];
Vector4 expected;
} MultiDrawInstancedData[] {
{"all zero vertex counts", false, false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{0, 0},
{1, 1},
{0, 0},
{0, 0},
{0.0f, 0.0f, 0.0f, 0.0f}},
{"all zero instance counts", false, false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{1, 1},
{0, 0},
{0, 0},
{0, 0},
{0.0f, 0.0f, 0.0f, 0.0f}},
{"single draw", false, false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{2, 0},
{2, 0},
{0, 0},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#ifndef MAGNUM_TARGET_GLES2
{"single draw, vertex ID", true, false,
{{0.25f, 0.75f, 0.0f},
{0.0f, 0.5f, 1.0f}},
{2, 0},
{2, 0},
{0, 0},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#endif
{"single draw, draw ID", false, true,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{2, 0},
{2, 0},
{0, 0},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
{"multi draw", false, false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{1, 1},
{2, 2},
{0, 1},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#ifndef MAGNUM_TARGET_GLES2
{"multi draw, vertex ID", true, false,
{{0.25f, 0.75f, 0.0f},
{0.0f, 0.5f, 1.0f}},
{1, 1},
{2, 2},
{0, 1},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#endif
{"multi draw, draw ID", false, true,
{{0.25f, 0.75f, 0.0f},
{0.0f, 0.5f, 1.0f}},
{1, 1},
{2, 2},
{0, 1},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#ifndef MAGNUM_TARGET_GLES2
{"multi draw, instance offset", false, false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{2, 2},
{1, 1},
{0, 0},
{0, 1},
{0.25f, 0.5f, 0.75f, 1.0f}}
#endif
};
const struct {
const char* name;
bool vertexId;
Vector3 values[2];
UnsignedInt indices[2];
UnsignedInt counts[2];
UnsignedInt instanceCounts[2];
UnsignedInt indexOffsetsInBytes[2];
UnsignedInt vertexOffsets[2];
UnsignedInt instanceOffsets[2];
Vector4 expected;
} MultiDrawInstancedIndexedData[] {
{"single draw", false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{0, 1},
{2, 0},
{2, 0},
{0, 0},
{0, 0},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
{"multi draw", false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{0, 1},
{1, 1},
{2, 2},
{0, 4},
{0, 0},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#ifndef MAGNUM_TARGET_GLES2
{"multi draw, vertex offset", false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{0, 0},
{1, 1},
{2, 2},
{0, 0},
{0, 1},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
{"multi draw, vertex offset, vertex ID", true,
{{0.25f, 0.75f, 0.0f},
{0.0f, 0.5f, 1.0f}},
/* Same as in the non-indexed case, gl_VertexID includes the baseVertex
as well */
{0, 0},
{1, 1},
{2, 2},
{0, 0},
{0, 1},
{0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
{"multi draw, instance offset", false,
{{0.25f, 0.75f, 0.0f},
{0.5f, 1.0f, 0.0f}},
{0, 1},
{2, 2},
{1, 1},
{0, 0},
{0, 0},
{0, 1},
{0.25f, 0.5f, 0.75f, 1.0f}},
#endif
};
#endif
using namespace Math::Literals;
MeshGLTest::MeshGLTest() {
@ -519,6 +701,39 @@ MeshGLTest::MeshGLTest() {
&MeshGLTest::multiDrawViewsDifferentMeshes
});
#ifdef MAGNUM_TARGET_GLES
addInstancedTests({&MeshGLTest::multiDrawInstanced,
&MeshGLTest::multiDrawInstancedSparseArrays},
Containers::arraySize(MultiDrawInstancedData));
addInstancedTests<MeshGLTest>({
&MeshGLTest::multiDrawInstancedIndexed<UnsignedInt>,
#ifndef CORRADE_TARGET_32BIT
&MeshGLTest::multiDrawInstancedIndexed<UnsignedLong>,
#endif
&MeshGLTest::multiDrawInstancedIndexedSparseArrays<UnsignedInt>,
#ifndef CORRADE_TARGET_32BIT
&MeshGLTest::multiDrawInstancedIndexedSparseArrays<UnsignedLong>
#endif
}, Containers::arraySize(MultiDrawInstancedIndexedData));
addTests({
&MeshGLTest::multiDrawInstancedWrongInstanceCountSize,
&MeshGLTest::multiDrawInstancedWrongVertexOffsetSize,
#ifndef MAGNUM_TARGET_GLES2
&MeshGLTest::multiDrawInstancedWrongInstanceOffsetSize,
#endif
&MeshGLTest::multiDrawInstancedIndexedWrongInstanceCountSize,
&MeshGLTest::multiDrawInstancedIndexedWrongVertexOffsetSize,
&MeshGLTest::multiDrawInstancedIndexedWrongIndexOffsetSize,
#ifndef MAGNUM_TARGET_GLES2
&MeshGLTest::multiDrawInstancedIndexedWrongInstanceOffsetSize,
&MeshGLTest::multiDrawInstancedBaseVertexNoExtensionAvailable,
&MeshGLTest::multiDrawInstancedBaseInstanceNoExtensionAvailable
#endif
});
#endif
/* Reset clear color to something trivial first */
Renderer::setClearColor(0x000000_rgbf);
}
@ -4060,6 +4275,7 @@ void MeshGLTest::multiDrawIndexedWrongVertexOffsetSize() {
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, vertexOffsets, indexOffsets);
/* Omitting vertex offsets altogether is okay */
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 vertex offset items but got 2\n");
}
@ -4198,6 +4414,682 @@ void MeshGLTest::multiDrawViewsDifferentMeshes() {
CORRADE_COMPARE(out.str(), Utility::formatString("GL::AbstractShaderProgram::draw(): all meshes must be views of the same original mesh, expected 0x{:x} but got 0x{:x} at index 1\n", reinterpret_cast<std::uintptr_t>(&a), reinterpret_cast<std::uintptr_t>(&b)));
}
#ifdef MAGNUM_TARGET_GLES
struct MultiDrawInstancedShader: AbstractShaderProgram {
typedef Attribute<0, Float> PositionX;
typedef Attribute<1, Float> PositionY;
typedef Attribute<2, Vector3> Value;
#ifdef MAGNUM_TARGET_GLES2
/* ES2 has no integer attributes either */
typedef Attribute<3, Float> InstanceId;
#endif
explicit MultiDrawInstancedShader(bool vertexId = false, bool drawId = false
#ifndef MAGNUM_TARGET_GLES2
, bool instanceOffset = false
#endif
);
};
MultiDrawInstancedShader::MultiDrawInstancedShader(bool vertexId, bool drawId
#ifndef MAGNUM_TARGET_GLES2
, bool instanceOffset
#endif
) {
/* Pick GLSL 3.0 / ESSL 3.0 for gl_VertexID, if available */
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
const Version version = Context::current().supportedVersion({Version::GL300, Version::GL210});
#else
const Version version = Version::GL310;
#endif
#else
const Version version = Context::current().supportedVersion({Version::GLES300, Version::GLES200});
#endif
Shader vert{version, Shader::Type::Vertex};
Shader frag{version, Shader::Type::Fragment};
if(drawId) vert.addSource(
"#extension GL_ANGLE_multi_draw: require\n"
"#define vertexOrDrawIdOrInstanceOffset gl_DrawID\n");
else if(vertexId) vert.addSource(
"#define vertexOrDrawIdOrInstanceOffset gl_VertexID\n");
#ifndef MAGNUM_TARGET_GLES2
else if(instanceOffset) vert.addSource(
"#extension GL_ANGLE_base_vertex_base_instance: require\n"
"#define vertexOrDrawIdOrInstanceOffset gl_BaseInstance\n");
#endif
else vert.addSource(
"#define vertexOrDrawIdOrInstanceOffset 0\n");
vert.addSource(
"#if defined(GL_ES) && __VERSION__ == 100\n"
"#define in attribute\n"
"#define out varying\n"
"#endif\n"
"in mediump float positionX;\n"
"in mediump float positionY;\n"
"in mediump vec3 value;\n"
#ifdef MAGNUM_TARGET_GLES2
"in mediump float instanceId;\n"
#endif
"out mediump float valueInterpolated;\n"
"void main() {\n"
#ifndef MAGNUM_TARGET_GLES2
" valueInterpolated = value[vertexOrDrawIdOrInstanceOffset + gl_InstanceID];\n"
#else
" valueInterpolated = value[vertexOrDrawIdOrInstanceOffset + int(instanceId)];\n"
#endif
" gl_Position = vec4(positionX, positionY, 0.0, 1.0);\n"
"}\n");
frag.addSource(
"#if defined(GL_ES) && __VERSION__ == 100\n"
"#define in varying\n"
"#define result gl_FragColor\n"
"#endif\n"
"in mediump float valueInterpolated;\n"
"#if defined(GL_ES) && __VERSION__ >= 300\n"
"out mediump vec4 result;\n"
"#endif\n"
"void main() { result.r = valueInterpolated; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
attachShaders({vert, frag});
bindAttributeLocation(PositionX::Location, "positionX");
bindAttributeLocation(PositionY::Location, "positionY");
bindAttributeLocation(Value::Location, "value");
#ifdef MAGNUM_TARGET_GLES2
bindAttributeLocation(InstanceId::Location, "instanceId");
#endif
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
void MeshGLTest::multiDrawInstanced() {
auto&& data = MultiDrawInstancedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_WEBGL
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
bool hasBaseInstance = data.instanceOffsets[0] || data.instanceOffsets[1];
if(hasBaseInstance) {
#ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_WEBGL
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::base_vertex_base_instance>())
CORRADE_SKIP(GL::Extensions::ANGLE::base_vertex_base_instance::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() << "is not supported.");
#endif
#else
CORRADE_FAIL_IF(false, "Can't do base instance here.");
#endif
}
const struct {
Float positionX;
Vector3 value;
} vertexData[] {
{}, /* initial offset */
{-1.0f/3.0f, data.values[0]},
{ 1.0f/3.0f, data.values[1]},
};
const Float instanceData[]{
{}, /* initial offset */
-1.0f/3.0f,
1.0f/3.0f
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawInstancedShader::PositionX{}, MultiDrawInstancedShader::Value{})
.addVertexBufferInstanced(Buffer{instanceData}, 1, sizeof(instanceData[0]), MultiDrawInstancedShader::PositionY{});
#ifdef MAGNUM_TARGET_GLES2
/* Because ANGLE_instanced_arrays on ES2 / WebGL 1 doesn't even provide
gl_InstanceID ... and there are no integer attributes either */
const Float instanceId[]{0.0f, 1.0f};
mesh.addVertexBufferInstanced(Buffer{instanceId}, 1, 0, MultiDrawInstancedShader::InstanceId{});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
MultiDrawChecker checker;
MultiDrawInstancedShader{data.vertexId, data.drawId
#ifndef MAGNUM_TARGET_GLES2
, hasBaseInstance
#endif
}.draw(mesh, data.counts, data.instanceCounts, data.vertexOffsets, nullptr
#ifndef MAGNUM_TARGET_GLES2
, hasBaseInstance ? Containers::arrayView(data.instanceOffsets) : nullptr
#endif
);
Vector4 value = checker.get();
MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE_WITH(value, data.expected,
TestSuite::Compare::around(Vector4{1.0f/255.0f}));
#else
CORRADE_COMPARE_WITH(value, data.expected, /* it's only RGBA4 */
TestSuite::Compare::around(Vector4{1.0f/15.0f}));
#endif
}
void MeshGLTest::multiDrawInstancedSparseArrays() {
auto&& data = MultiDrawInstancedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_WEBGL
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
bool hasBaseInstance = data.instanceOffsets[0] || data.instanceOffsets[1];
if(hasBaseInstance) {
#ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_WEBGL
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::base_vertex_base_instance>())
CORRADE_SKIP(GL::Extensions::ANGLE::base_vertex_base_instance::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() << "is not supported.");
#endif
#else
CORRADE_FAIL_IF(false, "Can't do base instance here.");
#endif
}
const struct {
Float positionX;
Vector3 value;
} vertexData[] {
{}, /* initial offset */
{-1.0f/3.0f, data.values[0]},
{ 1.0f/3.0f, data.values[1]},
};
const Float instanceData[]{
{}, /* initial offset */
-1.0f/3.0f,
1.0f/3.0f
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawInstancedShader::PositionX{}, MultiDrawInstancedShader::Value{})
.addVertexBufferInstanced(Buffer{instanceData}, 1, sizeof(instanceData[0]), MultiDrawInstancedShader::PositionY{});
#ifdef MAGNUM_TARGET_GLES2
/* Because ANGLE_instanced_arrays on ES2 / WebGL 1 doesn't even provide
gl_InstanceID ... and there are no integer attributes either */
const Float instanceId[]{0.0f, 1.0f};
mesh.addVertexBufferInstanced(Buffer{instanceId}, 1, 0, MultiDrawInstancedShader::InstanceId{});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
/* The signature accepted by glMultiDrawArraysIndirect() */
struct Command {
UnsignedInt count;
UnsignedInt instanceCount;
UnsignedInt first;
UnsignedInt baseInstance;
} commands[] {
{data.counts[0], data.instanceCounts[0], data.vertexOffsets[0], data.instanceOffsets[0]},
{data.counts[1], data.instanceCounts[1], data.vertexOffsets[1], data.instanceOffsets[1]}
};
MultiDrawChecker checker;
MultiDrawInstancedShader{data.vertexId, data.drawId
#ifndef MAGNUM_TARGET_GLES2
, hasBaseInstance
#endif
}.draw(mesh,
Containers::stridedArrayView(commands).slice(&Command::count),
Containers::stridedArrayView(commands).slice(&Command::instanceCount),
Containers::stridedArrayView(commands).slice(&Command::first),
nullptr
#ifndef MAGNUM_TARGET_GLES2
,
hasBaseInstance ? Containers::stridedArrayView(commands).slice(&Command::baseInstance) : nullptr
#endif
);
Vector4 value = checker.get();
MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE_WITH(value, data.expected,
TestSuite::Compare::around(Vector4{1.0f/255.0f}));
#else
CORRADE_COMPARE_WITH(value, data.expected, /* it's only RGBA4 */
TestSuite::Compare::around(Vector4{1.0f/15.0f}));
#endif
}
template<class T> void MeshGLTest::multiDrawInstancedIndexed() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
auto&& data = MultiDrawInstancedIndexedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_WEBGL
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
bool hasBaseVertex = data.vertexOffsets[0] || data.vertexOffsets[1];
bool hasBaseInstance = data.instanceOffsets[0] || data.instanceOffsets[1];
if(hasBaseVertex || hasBaseInstance) {
#ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::ANGLE::base_vertex_base_instance>())
CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() << "is not supported.");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() << "is not supported.");
#endif
#else
CORRADE_FAIL_IF(false, "Can't do base vertex or base instance here.");
#endif
}
const struct {
Float positionX;
Vector3 value;
} vertexData[] {
{}, /* initial offset */
{-1.0f/3.0f, data.values[0]},
{ 1.0f/3.0f, data.values[1]},
};
const Float instanceData[]{
{}, /* initial offset */
-1.0f/3.0f,
1.0f/3.0f
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawInstancedShader::PositionX{}, MultiDrawInstancedShader::Value{})
.addVertexBufferInstanced(Buffer{instanceData}, 1, sizeof(instanceData[0]), MultiDrawInstancedShader::PositionY{})
.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, data.indices}, 0, MeshIndexType::UnsignedInt);
#ifdef MAGNUM_TARGET_GLES2
/* Because ANGLE_instanced_arrays on ES2 / WebGL 1 doesn't even provide
gl_InstanceID ... and there are no integer attributes either */
const Float instanceId[]{0.0f, 1.0f};
mesh.addVertexBufferInstanced(Buffer{instanceId}, 1, 0, MultiDrawInstancedShader::InstanceId{});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
/* Converted to either a 32bit or 64bit type */
const T indexOffsetsInBytes[]{
data.indexOffsetsInBytes[0],
data.indexOffsetsInBytes[1]
};
MultiDrawChecker checker;
MultiDrawInstancedShader{data.vertexId, false
#ifndef MAGNUM_TARGET_GLES2
, hasBaseInstance
#endif
}.draw(mesh, data.counts, data.instanceCounts, hasBaseVertex ? Containers::arrayView(data.vertexOffsets) : nullptr, indexOffsetsInBytes
#ifndef MAGNUM_TARGET_GLES2
, hasBaseInstance ? Containers::arrayView(data.instanceOffsets) : nullptr
#endif
);
Vector4 value = checker.get();
MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE_WITH(value, data.expected,
TestSuite::Compare::around(Vector4{1.0f/255.0f}));
#else
CORRADE_COMPARE_WITH(value, data.expected, /* it's only RGBA4 */
TestSuite::Compare::around(Vector4{1.0f/15.0f}));
#endif
}
template<class T> void MeshGLTest::multiDrawInstancedIndexedSparseArrays() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
auto&& data = MultiDrawInstancedIndexedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_WEBGL
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
bool hasBaseVertex = data.vertexOffsets[0] || data.vertexOffsets[1];
bool hasBaseInstance = data.instanceOffsets[0] || data.instanceOffsets[1];
if(hasBaseVertex || hasBaseInstance) {
#ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::ANGLE::base_vertex_base_instance>())
CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() << "is not supported.");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() << "is not supported.");
#endif
#else
CORRADE_FAIL_IF(false, "Can't do base vertex or base instance here.");
#endif
}
const struct {
Float positionX;
Vector3 value;
} vertexData[] {
{}, /* initial offset */
{-1.0f/3.0f, data.values[0]},
{ 1.0f/3.0f, data.values[1]},
};
const Float instanceData[]{
{}, /* initial offset */
-1.0f/3.0f,
1.0f/3.0f
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawInstancedShader::PositionX{}, MultiDrawInstancedShader::Value{})
.addVertexBufferInstanced(Buffer{instanceData}, 1, sizeof(instanceData[0]), MultiDrawInstancedShader::PositionY{})
.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, data.indices}, 0, MeshIndexType::UnsignedInt);
#ifdef MAGNUM_TARGET_GLES2
/* Because ANGLE_instanced_arrays on ES2 / WebGL 1 doesn't even provide
gl_InstanceID ... and there are no integer attributes either */
const Float instanceId[]{0.0f, 1.0f};
mesh.addVertexBufferInstanced(Buffer{instanceId}, 1, 0, MultiDrawInstancedShader::InstanceId{});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
/* The signature accepted by glMultiDrawElementsIndirect() EXCEPT that
here we need firstIndex to be in bytes */
struct Command {
UnsignedInt count;
UnsignedInt instanceCount;
T firstIndexInBytes; /* !! */
UnsignedInt baseVertex;
UnsignedInt baseInstance;
} commands[] {
{data.counts[0], data.instanceCounts[0], data.indexOffsetsInBytes[0], data.vertexOffsets[0], data.instanceOffsets[0]},
{data.counts[1], data.instanceCounts[1], data.indexOffsetsInBytes[1], data.vertexOffsets[1], data.instanceOffsets[1]}
};
MultiDrawChecker checker;
MultiDrawInstancedShader{data.vertexId, false
#ifndef MAGNUM_TARGET_GLES2
, hasBaseInstance
#endif
}.draw(mesh,
Containers::stridedArrayView(commands).slice(&Command::count),
Containers::stridedArrayView(commands).slice(&Command::instanceCount),
hasBaseVertex ? Containers::stridedArrayView(commands).slice(&Command::baseVertex) : nullptr,
Containers::stridedArrayView(commands).slice(&Command::firstIndexInBytes)
#ifndef MAGNUM_TARGET_GLES2
,
hasBaseInstance ? Containers::stridedArrayView(commands).slice(&Command::baseInstance) : nullptr
#endif
);
Vector4 value = checker.get();
MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE_WITH(value, data.expected,
TestSuite::Compare::around(Vector4{1.0f/255.0f}));
#else
CORRADE_COMPARE_WITH(value, data.expected, /* it's only RGBA4 */
TestSuite::Compare::around(Vector4{1.0f/15.0f}));
#endif
}
void MeshGLTest::multiDrawInstancedWrongInstanceCountSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, nullptr, nullptr);
shader.draw(mesh, counts, nullptr, nullptr, nullptr);
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 instance count items but got 2\n"
"GL::AbstractShaderProgram::draw(): expected 3 instance count items but got 0\n");
}
void MeshGLTest::multiDrawInstancedWrongVertexOffsetSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[3]{};
UnsignedInt vertexOffsets[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, vertexOffsets, nullptr);
shader.draw(mesh, counts, instanceCounts, nullptr, nullptr);
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 vertex offset items but got 2\n"
"GL::AbstractShaderProgram::draw(): expected 3 vertex offset items but got 0\n");
}
#ifndef MAGNUM_TARGET_GLES2
void MeshGLTest::multiDrawInstancedWrongInstanceOffsetSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[3]{};
UnsignedInt vertexOffsets[3]{};
UnsignedInt instanceOffsets[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, vertexOffsets, nullptr, instanceOffsets);
/* Omitting vertex offsets altogether is okay */
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): expected 3 instance offset items but got 2\n");
}
#endif
void MeshGLTest::multiDrawInstancedIndexedWrongInstanceCountSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, nullptr, nullptr);
shader.draw(mesh, counts, nullptr, nullptr, nullptr);
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 instance count items but got 2\n"
"GL::AbstractShaderProgram::draw(): expected 3 instance count items but got 0\n");
}
void MeshGLTest::multiDrawInstancedIndexedWrongVertexOffsetSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[3]{};
UnsignedInt vertexOffsets[2]{};
UnsignedInt indexOffsets[3]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets);
/* Omitting vertex offsets altogether is okay */
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 vertex offset items but got 2\n");
}
void MeshGLTest::multiDrawInstancedIndexedWrongIndexOffsetSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[3]{};
UnsignedInt indexOffsets[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, nullptr, indexOffsets);
shader.draw(mesh, counts, instanceCounts, nullptr, nullptr);
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 index offset items but got 2\n"
"GL::AbstractShaderProgram::draw(): expected 3 index offset items but got 0\n");
}
#ifndef MAGNUM_TARGET_GLES2
void MeshGLTest::multiDrawInstancedIndexedWrongInstanceOffsetSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
MultiDrawInstancedShader shader;
UnsignedInt counts[3]{};
UnsignedInt instanceCounts[3]{};
UnsignedInt indexOffsets[3]{};
UnsignedInt instanceOffsets[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, instanceCounts, nullptr, indexOffsets, instanceOffsets);
/* Omitting instance offsets altogether is okay */
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): expected 3 instance offset items but got 2\n");
}
void MeshGLTest::multiDrawInstancedBaseVertexNoExtensionAvailable() {
/* The top-level multidraw extension isn't guarded (the user is expected to
do so), but the base vertex is as the conditions are more complex */
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(Extensions::ANGLE::multi_draw::string() << "is not supported.");
if(Context::current().isExtensionSupported<Extensions::ANGLE::base_vertex_base_instance>())
CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() << "is supported.");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw::string() << "is not supported.");
if(Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() << "is supported.");
#endif
Mesh mesh;
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
UnsignedInt counts[]{3};
UnsignedInt instanceCounts[]{3};
UnsignedInt vertexOffsets[]{0};
UnsignedInt indexOffsets[]{0};
std::ostringstream out;
Error redirectError{&out};
MultiDrawInstancedShader{}.draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets);
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh multi-draw with base vertex and base instance specification\n");
#else
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): instanced indexed mesh multi-draw with base vertex specification possible only since OpenGL ES 3.0 and WebGL 2.0\n");
#endif
}
void MeshGLTest::multiDrawInstancedBaseInstanceNoExtensionAvailable() {
/* The top-level multidraw extension isn't guarded (the user is expected to
do so), but the base vertex is as the conditions are more complex */
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(Extensions::ANGLE::multi_draw::string() << "is not supported.");
if(Context::current().isExtensionSupported<Extensions::ANGLE::base_vertex_base_instance>())
CORRADE_SKIP(Extensions::ANGLE::base_vertex_base_instance::string() << "is supported.");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw::string() << "is not supported.");
if(Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() << "is supported.");
#endif
Mesh nonIndexed;
Mesh indexed;
indexed.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
UnsignedInt counts[]{3};
UnsignedInt instanceCounts[]{3};
UnsignedInt vertexOffsets[]{0};
UnsignedInt indexOffsets[]{0};
UnsignedInt instanceOffsets[]{0};
std::ostringstream out;
Error redirectError{&out};
MultiDrawInstancedShader{}.draw(nonIndexed, counts, instanceCounts, vertexOffsets, nullptr, instanceOffsets);
MultiDrawInstancedShader{}.draw(indexed, counts, instanceCounts, nullptr, indexOffsets, instanceOffsets);
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): no extension available for instanced mesh multi-draw with base instance specification\n"
"GL::AbstractShaderProgram::draw(): no extension available for instanced indexed mesh multi-draw with base vertex and base instance specification\n");
}
#endif
#endif
}}}}
CORRADE_TEST_MAIN(Magnum::GL::Test::MeshGLTest)

Loading…
Cancel
Save