Browse Source

GL: AbstractShaderProgram::draw() that takes plain array views.

Because a MeshView might not be the best thing to have when you are
submitting a batch of thousand draws. It takes a strided array views to
allow for more flexibility, but can also detect if the input is already
contiguous and use it as-is.

UNFORTUNATELY the GL 1.0 legacy still continues to stink and so there
has to be a 64-bit-specific overload which is the *actual* variant that
doesn't allocate because glMultiDrawElements takes a `void**` for INDEX
OFFSETS and it's IN BYTES! Which foolish soul designed such a thing back
in the 1860s, I wonder. There's no reason to not have an index offset
in elements because all indices have to have the same type ANYWAY. And
yes, I wasted about three hours debugging driver crashes because I
THOUGHT this parameter takes offset in elements, not bytes.

Also note: on 32-bit platforms this depends on latest Corrade with the
CORRADE_TARGET_32BIT definition. Spent an embarrassing amount of time
wondering why all local builds but Emscripten work.
pull/537/head
Vladimír Vondruš 5 years ago
parent
commit
c2ecaa6abf
  1. 3
      doc/changelog.dox
  2. 35
      src/Magnum/GL/AbstractShaderProgram.cpp
  3. 91
      src/Magnum/GL/AbstractShaderProgram.h
  4. 14
      src/Magnum/GL/Implementation/MeshState.cpp
  5. 6
      src/Magnum/GL/Implementation/MeshState.h
  6. 201
      src/Magnum/GL/Mesh.cpp
  7. 19
      src/Magnum/GL/Mesh.h
  8. 79
      src/Magnum/GL/MeshView.cpp
  9. 458
      src/Magnum/GL/Test/MeshGLTest.cpp
  10. 11
      src/Magnum/Shaders/DistanceFieldVectorGL.h
  11. 11
      src/Magnum/Shaders/FlatGL.h
  12. 22
      src/Magnum/Shaders/MeshVisualizerGL.h
  13. 11
      src/Magnum/Shaders/PhongGL.h
  14. 11
      src/Magnum/Shaders/VectorGL.h
  15. 11
      src/Magnum/Shaders/VertexColorGL.h

3
doc/changelog.dox

@ -61,6 +61,9 @@ See also:
@subsubsection changelog-latest-new-gl GL library
- 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
- 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

35
src/Magnum/GL/AbstractShaderProgram.cpp

@ -26,6 +26,7 @@
#include "AbstractShaderProgram.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/DebugStl.h>
@ -382,6 +383,38 @@ AbstractShaderProgram& AbstractShaderProgram::draw(MeshView& mesh) {
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
if(!counts.size()) return *this;
use();
#ifndef MAGNUM_TARGET_GLES
Mesh::multiDrawImplementationDefault(mesh, counts, vertexOffsets, indexOffsets);
#else
Context::current().state().mesh.multiDrawImplementation(mesh, counts, vertexOffsets, indexOffsets);
#endif
return *this;
}
#ifndef CORRADE_TARGET_32BIT
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
if(!counts.size()) return *this;
use();
#ifndef MAGNUM_TARGET_GLES
Mesh::multiDrawImplementationDefault(mesh, counts, vertexOffsets, indexOffsets);
#else
Context::current().state().mesh.multiDrawLongImplementation(mesh, counts, vertexOffsets, indexOffsets);
#endif
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return draw(mesh, counts, vertexOffsets, Containers::StridedArrayView1D<const UnsignedLong>{});
}
#endif
AbstractShaderProgram& AbstractShaderProgram::draw(Containers::ArrayView<const Containers::Reference<MeshView>> meshes) {
if(meshes.empty()) return *this;
@ -396,7 +429,7 @@ AbstractShaderProgram& AbstractShaderProgram::draw(Containers::ArrayView<const C
#ifndef MAGNUM_TARGET_GLES
MeshView::multiDrawImplementationDefault(meshes);
#else
Context::current().state().mesh.multiDrawImplementation(meshes);
Context::current().state().mesh.multiDrawViewImplementation(meshes);
#endif
return *this;
}

91
src/Magnum/GL/AbstractShaderProgram.h

@ -844,31 +844,106 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
}
/**
* @brief Draw multiple meshes at once
* @brief Draw multiple mesh views at once
* @param mesh The mesh from which to draw. Expected to not be
* instanced.
* @param counts Vertex/index counts for each draw.
* @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.
* @return Reference to self (for method chaining)
* @m_since{2020,06}
* @m_since_latest
*
* On OpenGL ES, if neither @gl_extension{EXT,multi_draw_arrays} nor
* @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* is present, and on WebGL if @webgl_extension{WEBGL,multi_draw} is
* not present, the functionality is emulated using a sequence of
* @ref draw(MeshView&) calls. Note that @webgl_extension{WEBGL,multi_draw}
* is only implemented since Emscripten 2.0.0, so it's not even
* advertised on older versions.
* not present, the functionality is emulated equivalently to a
* sequence of @ref draw(MeshView&) calls with items of @p counts used
* for @ref MeshView::setCount(), @p vertexOffsets for
* @ref MeshView::setBaseVertex() and @p indexOffsets divided by size
* of the index type for @ref MeshView::setIndexRange(). Note that
* @webgl_extension{WEBGL,multi_draw} is only implemented since
* Emscripten 2.0.0, so it's not even advertised on older versions.
*
* If @gl_extension{ARB,vertex_array_object} (part of OpenGL 3.0),
* 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.
* @attention All meshes must be views of the same original mesh and
* must not be instanced.
*
* If @p counts, @p vertexOffsets and @p indexOffsets are contiguous
* views, they get passed directly to the underlying GL functions,
* otherwise a temporary contiguous copy is allocated. On 64-bit
* systems the @p indexOffsets additionally have to be 64-bit in order
* to avoid a copy because the @fn_gl{MultiDrawElements} function
* accepts them as pointers, see the
* @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedLong>&)
* overload below.
*
* @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}, @fn_gl_keyword{MultiDrawArrays} or
* @fn_gl_keyword{MultiDrawElements}/@fn_gl_keyword{MultiDrawElementsBaseVertex}
* @requires_gl32 Extension @gl_extension{ARB,draw_elements_base_vertex}
* if the mesh is indexed and the @p baseVertex view is not empty.
* @requires_es_extension OpenGL ES 3.0 and extension
* @gl_extension{OES,draw_elements_base_vertex} or
* @gl_extension{EXT,draw_elements_base_vertex} if the mesh is
* indexed and the @p baseVertex view is not empty.
* @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 baseVertex view is not empty.
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets);
#ifndef CORRADE_TARGET_32BIT
/**
* @brief Draw multiple mesh views at once
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Defined only on 64-bit builds. Compared to @ref draw(Mesh&, 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 @fn_gl{MultiDrawElements} function and can instead directly
* reuse the @p indexOffsets view if it's contiguous.
*/
AbstractShaderProgram& draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, 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>& vertexOffsets, std::nullptr_t);
#endif
/**
* @brief Draw multiple mesh views at once
* @return Reference to self (for method chaining)
* @m_since{2020,06}
*
* Extracts the vertex/index counts, vertex offsets and index offsets
* out of the mesh list and then calls
* @ref draw(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&)
* (or @ref draw(Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) on 64-bit builds).
*
* On OpenGL ES, if neither @gl_extension{EXT,multi_draw_arrays} nor
* @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* is present, and on WebGL if @webgl_extension{WEBGL,multi_draw} is
* not present, the functionality is instead emulated using a sequence
* of @ref draw(MeshView&) calls. Note that
* @webgl_extension{WEBGL,multi_draw} is only implemented since
* 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.
*
* @requires_gl32 Extension @gl_extension{ARB,draw_elements_base_vertex}
* if the mesh is indexed and @ref MeshView::baseVertex() is not
* `0`
* @requires_es_extension OpenGL ES 3.0 and extension

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

@ -301,9 +301,19 @@ MeshState::MeshState(Context& context, ContextState& contextState, Containers::S
}
#endif
multiDrawImplementation = &MeshView::multiDrawImplementationDefault;
multiDrawImplementation = &Mesh::multiDrawImplementationDefault;
#ifndef CORRADE_TARGET_32BIT
multiDrawLongImplementation = &Mesh::multiDrawImplementationDefault;
#endif
multiDrawViewImplementation = &MeshView::multiDrawImplementationDefault;
} else multiDrawImplementation = &MeshView::multiDrawImplementationFallback;
} else {
multiDrawImplementation = &Mesh::multiDrawImplementationFallback;
#ifndef CORRADE_TARGET_32BIT
multiDrawLongImplementation = &Mesh::multiDrawImplementationFallback;
#endif
multiDrawViewImplementation = &MeshView::multiDrawImplementationFallback;
}
#endif
#ifdef MAGNUM_TARGET_GLES2

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

@ -77,7 +77,11 @@ struct MeshState {
#endif
#ifdef MAGNUM_TARGET_GLES
void(*multiDrawImplementation)(Containers::ArrayView<const Containers::Reference<MeshView>>);
void(*multiDrawImplementation)(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&);
#ifndef CORRADE_TARGET_32BIT
void(*multiDrawLongImplementation)(Mesh&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const UnsignedLong>&);
#endif
void(*multiDrawViewImplementation)(Containers::ArrayView<const Containers::Reference<MeshView>>);
void(APIENTRY *multiDrawArraysImplementation)(GLenum, const GLint*, const GLsizei*, GLsizei);
void(APIENTRY *multiDrawElementsImplementation)(GLenum, const GLsizei*, GLenum, const void* const*, GLsizei);
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))

201
src/Magnum/GL/Mesh.cpp

@ -26,6 +26,8 @@
#include "Mesh.h"
#include <vector>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/Debug.h>
#include "Magnum/Mesh.h"
@ -401,6 +403,205 @@ Mesh& Mesh::setIndexBuffer(Buffer& buffer, const GLintptr offset, const MeshInde
return *this;
}
void Mesh::drawInternal(const Containers::ArrayView<const UnsignedInt>& counts, const Containers::ArrayView<const UnsignedInt>& vertexOffsets,
#ifdef CORRADE_TARGET_32BIT
const Containers::ArrayView<const UnsignedInt>& indexOffsets
#else
const Containers::ArrayView<const UnsignedLong>& indexOffsets
#endif
) {
/* Not asserting for _instanceCount == 1, as this is *not* taken from the
original mesh, the counts/vertexOffsets/indexOffsets completely describe
the range being drawn */
const Implementation::MeshState& state = Context::current().state().mesh;
(this->*state.bindImplementation)();
/* Non-indexed meshes */
if(!_indexBuffer.id()) {
CORRADE_ASSERT(vertexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "vertex offset items but got" << vertexOffsets.size(), );
#ifndef MAGNUM_TARGET_GLES
glMultiDrawArrays
#else
state.multiDrawArraysImplementation
#endif
(GLenum(_primitive), reinterpret_cast<const GLint*>(vertexOffsets.data()), reinterpret_cast<const GLsizei*>(counts.data()), counts.size());
/* 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_GLES
glMultiDrawElements
#else
state.multiDrawElementsImplementation
#endif
(GLenum(_primitive), reinterpret_cast<const GLsizei*>(counts.data()), GLenum(_indexType), reinterpret_cast<const void* const*>(indexOffsets.data()), counts.size());
/* Indexed meshes with base vertex */
} else {
CORRADE_ASSERT(vertexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "vertex offset items but got" << vertexOffsets.size(), );
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
#ifndef MAGNUM_TARGET_GLES
glMultiDrawElementsBaseVertex
#else
state.multiDrawElementsBaseVertexImplementation
#endif
(GLenum(_primitive), reinterpret_cast<const GLsizei*>(counts.data()), GLenum(_indexType), reinterpret_cast<const void* const*>(indexOffsets.data()), counts.size(), reinterpret_cast<const GLint*>(vertexOffsets.data()));
#else
CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): indexed mesh multi-draw with base vertex specification possible only since WebGL 2.0", );
#endif
}
}
(this->*state.unbindImplementation)();
}
void Mesh::multiDrawImplementationDefault(Mesh& self, 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
directly */
if(counts.isContiguous() && vertexOffsets.isContiguous() && indexOffsets.isContiguous())
return self.drawInternal(counts.asContiguous(), vertexOffsets.asContiguous(), indexOffsets.asContiguous());
#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> vertexOffsetsContiguous;
#ifdef CORRADE_TARGET_32BIT
Containers::ArrayView<UnsignedInt>
#else
Containers::ArrayView<UnsignedLong>
#endif
indexOffsetsContiguous;
Containers::ArrayTuple data{
{NoInit, counts.size(), countsContiguous},
{NoInit, vertexOffsets.size(), 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}
};
Utility::copy(counts, countsContiguous);
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
);
self.drawInternal(countsContiguous, vertexOffsetsContiguous, indexOffsetsContiguous);
}
#ifndef CORRADE_TARGET_32BIT
void Mesh::multiDrawImplementationDefault(Mesh& self, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
/* If all views are contiguous, call the implementation directly */
if(counts.isContiguous() && vertexOffsets.isContiguous() && indexOffsets.isContiguous())
return self.drawInternal(counts.asContiguous(), vertexOffsets.asContiguous(), indexOffsets.asContiguous());
/* Otherwise delegate into the 32-bit variant, which will allocate a
contiguous copy */
multiDrawImplementationDefault(self, counts, 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
]
);
}
#endif
#ifdef MAGNUM_TARGET_GLES
void Mesh::multiDrawImplementationFallback(Mesh& self, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
const UnsignedInt zero[1]{};
Containers::StridedArrayView1D<const UnsignedInt> indexOffsetsNeverEmpty;
Containers::StridedArrayView1D<const UnsignedInt> vertexOffsetsNeverEmpty;
if(self._indexBuffer.id()) {
CORRADE_ASSERT(indexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "index offset items but got" << indexOffsets.size(), );
indexOffsetsNeverEmpty = indexOffsets;
if(!vertexOffsets.empty()) {
CORRADE_ASSERT(vertexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "vertex offset items but got" << vertexOffsets.size(), );
vertexOffsetsNeverEmpty = vertexOffsets;
} else vertexOffsetsNeverEmpty = Containers::StridedArrayView1D<const UnsignedInt>{zero, counts.size(), 0};
} else {
CORRADE_ASSERT(vertexOffsets.size() == counts.size(),
"GL::AbstractShaderProgram::draw(): expected" << counts.size() << "vertex offset items but got" << vertexOffsets.size(), );
vertexOffsetsNeverEmpty = vertexOffsets;
indexOffsetsNeverEmpty = Containers::StridedArrayView1D<const UnsignedInt>{zero, counts.size(), 0};
}
for(std::size_t i = 0; i != counts.size(); ++i) {
if(!counts[i]) continue;
self.drawInternal(
counts[i], vertexOffsetsNeverEmpty[i], 1,
#ifndef MAGNUM_TARGET_GLES2
0, indexOffsetsNeverEmpty[i], 0, 0
#else
indexOffsetsNeverEmpty[i]
#endif
);
}
}
#ifndef CORRADE_TARGET_32BIT
void Mesh::multiDrawImplementationFallback(Mesh& self, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
/* Delegate straight into the 32-bit variant */
multiDrawImplementationFallback(self, counts, 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
]
);
}
#endif
#endif
#ifndef MAGNUM_TARGET_GLES2
void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, UnsignedInt baseInstance, GLintptr indexOffset, Int indexStart, Int indexEnd)
#else

19
src/Magnum/GL/Mesh.h

@ -1089,6 +1089,14 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
void drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr indexOffset);
#endif
void drawInternal(const Containers::ArrayView<const UnsignedInt>& counts, 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_GLES
void drawInternal(TransformFeedback& xfb, UnsignedInt stream, Int instanceCount);
#endif
@ -1146,6 +1154,17 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
void MAGNUM_GL_LOCAL unbindImplementationDefault();
void MAGNUM_GL_LOCAL unbindImplementationVAO();
MAGNUM_GL_LOCAL static void multiDrawImplementationDefault(Mesh& self, 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 static void multiDrawImplementationDefault(Mesh& self, 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 static void multiDrawImplementationFallback(Mesh& self, 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 static void multiDrawImplementationFallback(Mesh& self, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets);
#endif
#endif
#ifdef MAGNUM_TARGET_GLES
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
#if defined(MAGNUM_TARGET_WEBGL) && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 13915

79
src/Magnum/GL/MeshView.cpp

@ -88,66 +88,37 @@ MeshView& MeshView::draw(AbstractShaderProgram&& shader, TransformFeedback& xfb,
void MeshView::multiDrawImplementationDefault(Containers::ArrayView<const Containers::Reference<MeshView>> meshes) {
CORRADE_INTERNAL_ASSERT(meshes.size());
const Implementation::MeshState& state = Context::current().state().mesh;
Mesh& original = meshes.begin()->get()._original;
Containers::Array<GLsizei> count{meshes.size()};
Containers::Array<GLvoid*> indices{meshes.size()};
Containers::Array<GLint> baseVertex{meshes.size()};
Mesh& original = meshes[0]->_original;
/* Gather the parameters */
bool hasBaseVertex = false;
std::size_t i = 0;
for(MeshView& mesh: meshes) {
CORRADE_ASSERT(mesh._instanceCount == 1, "GL::AbstractShaderProgram::draw(): cannot multi-draw instanced meshes", );
count[i] = mesh._count;
indices[i] = reinterpret_cast<GLvoid*>(mesh._indexOffset);
baseVertex[i] = mesh._baseVertex;
if(mesh._baseVertex) hasBaseVertex = true;
++i;
}
(original.*state.bindImplementation)();
/* Non-indexed meshes */
if(!original._indexBuffer.id()) {
#ifndef MAGNUM_TARGET_GLES
glMultiDrawArrays
Containers::ArrayView<UnsignedInt> counts;
Containers::ArrayView<UnsignedInt> vertexOffsets;
Containers::ArrayView<
#ifndef CORRADE_TARGET_32BIT
UnsignedLong
#else
state.multiDrawArraysImplementation
UnsignedInt
#endif
(GLenum(original._primitive), baseVertex, count, meshes.size());
/* Indexed meshes */
} else {
/* Indexed meshes with base vertex */
if(hasBaseVertex) {
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
#ifndef MAGNUM_TARGET_GLES
glMultiDrawElementsBaseVertex
#else
state.multiDrawElementsBaseVertexImplementation
#endif
(GLenum(original._primitive), count, GLenum(original._indexType), indices, meshes.size(), baseVertex);
#else
CORRADE_ASSERT_UNREACHABLE("GL::AbstractShaderProgram::draw(): indexed mesh multi-draw with base vertex specification possible only since WebGL 2.0", );
#endif
/* Indexed meshes */
} else {
#ifndef MAGNUM_TARGET_GLES
glMultiDrawElements
#else
state.multiDrawElementsImplementation
#endif
(GLenum(original._primitive), count, GLenum(original._indexType), indices, meshes.size());
}
> indexOffsets;
Containers::ArrayTuple data{
{NoInit, meshes.size(), counts},
{NoInit, meshes.size(), vertexOffsets},
{NoInit, meshes.size(), indexOffsets}
};
/* The vertexOffsets array is used for non-indexed meshes or if the base
vertex is specified for any of the meshes */
bool useVertexOffsets = !original.isIndexed();
for(std::size_t i = 0; i != meshes.size(); ++i) {
CORRADE_ASSERT(meshes[i]->_instanceCount == 1, "GL::AbstractShaderProgram::draw(): cannot multi-draw instanced meshes", );
counts[i] = meshes[i]->_count;
vertexOffsets[i] = meshes[i]->_baseVertex;
indexOffsets[i] = meshes[i]->_indexOffset;
if(meshes[i]->_baseVertex) useVertexOffsets = true;
}
(original.*state.unbindImplementation)();
original.drawInternal(counts, useVertexOffsets ? vertexOffsets : nullptr, indexOffsets);
}
#ifdef MAGNUM_TARGET_GLES

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

@ -184,12 +184,20 @@ struct MeshGLTest: OpenGLTester {
void resetDivisorAfterInstancedDraw();
void multiDraw();
void multiDrawSparseArrays();
void multiDrawViews();
void multiDrawIndexed();
void multiDrawIndexedSparseArrays();
void multiDrawIndexedViews();
void multiDrawWrongVertexOffsetSize();
void multiDrawIndexedWrongIndexOffsetSize();
void multiDrawIndexedWrongVertexOffsetSize();
#ifdef MAGNUM_TARGET_GLES
void multiDrawIndexedBaseVertexNoExtensionAvailable();
void multiDrawIndexedViewsBaseVertexNoExtensionAvailable();
#endif
void multiDrawInstanced();
void multiDrawDifferentMeshes();
void multiDrawInstancedViews();
void multiDrawViewsDifferentMeshes();
};
const struct {
@ -280,7 +288,12 @@ const struct {
Vector4 values[4];
UnsignedInt indices[4];
UnsignedInt counts[4];
UnsignedInt indexOffsets[4];
#ifdef CORRADE_TARGET_32BIT
UnsignedInt
#else
UnsignedLong
#endif
indexOffsetsInBytes[4];
UnsignedInt vertexOffsets[4];
Vector4 expected;
} MultiDrawIndexedData[] {
@ -301,7 +314,7 @@ const struct {
{1.0f, 0.0f, 0.0f, 0.0f}},
{0, 1, 2, 3},
{1, 1, 1, 1},
{0, 1, 2, 3},
{0, 4, 8, 12},
{0, 0, 0, 0},
{0.25f, 0.5f, 0.75f, 1.0f}},
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
@ -312,7 +325,7 @@ const struct {
{1.0f, 0.0f, 0.0f, 0.0f}},
{0, 1, 0, 1},
{1, 1, 1, 1},
{0, 1, 2, 3},
{0, 4, 8, 12},
{0, 0, 2, 2},
{0.25f, 0.5f, 0.75f, 1.0f}},
#ifndef MAGNUM_TARGET_GLES2
@ -325,7 +338,7 @@ const struct {
{0.0f, 0.0f, 0.0f, 1.0f}},
{0, 1, 0, 1},
{1, 1, 1, 1},
{0, 1, 2, 3},
{0, 4, 8, 12},
{0, 0, 2, 2},
{0.25f, 0.5f, 0.75f, 1.0f}},
#endif
@ -337,7 +350,7 @@ const struct {
{1.0f, 0.0f, 0.0f, 0.0f}},
{5, 1, 0, 3},
{1, 0, 1, 1},
{3, 0, 2, 1},
{12, 0, 8, 4},
{0, 0, 0, 0},
/* The positions are fixed so this still renders in the same order */
{0.25f, 0.5f, 0.0f, 1.0f}}
@ -482,18 +495,26 @@ MeshGLTest::MeshGLTest() {
#endif
&MeshGLTest::resetDivisorAfterInstancedDraw});
addInstancedTests({&MeshGLTest::multiDraw},
addInstancedTests({&MeshGLTest::multiDraw,
&MeshGLTest::multiDrawSparseArrays,
&MeshGLTest::multiDrawViews},
Containers::arraySize(MultiDrawData));
addInstancedTests({&MeshGLTest::multiDrawIndexed},
addInstancedTests({&MeshGLTest::multiDrawIndexed,
&MeshGLTest::multiDrawIndexedSparseArrays,
&MeshGLTest::multiDrawIndexedViews},
Containers::arraySize(MultiDrawIndexedData));
addTests({
&MeshGLTest::multiDrawWrongVertexOffsetSize,
&MeshGLTest::multiDrawIndexedWrongIndexOffsetSize,
&MeshGLTest::multiDrawIndexedWrongVertexOffsetSize,
#ifdef MAGNUM_TARGET_GLES
&MeshGLTest::multiDrawIndexedBaseVertexNoExtensionAvailable,
&MeshGLTest::multiDrawIndexedViewsBaseVertexNoExtensionAvailable,
#endif
&MeshGLTest::multiDrawInstanced,
&MeshGLTest::multiDrawDifferentMeshes
&MeshGLTest::multiDrawInstancedViews,
&MeshGLTest::multiDrawViewsDifferentMeshes
});
/* Reset clear color to something trivial first */
@ -3550,6 +3571,148 @@ void MeshGLTest::multiDraw() {
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawShader::Position{}, MultiDrawShader::Value{});
MAGNUM_VERIFY_NO_GL_ERROR();
MultiDrawChecker checker;
MultiDrawShader{data.vertexId, data.drawId}.draw(mesh, data.counts, data.vertexOffsets, nullptr);
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::multiDrawSparseArrays() {
auto&& data = MultiDrawData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
if(data.drawId) {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::shader_draw_parameters>())
CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported.");
#elif !defined(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
}
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::EXT::multi_draw_arrays>() &&
!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_INFO("Neither" << Extensions::EXT::multi_draw_arrays::string() << "nor" << Extensions::ANGLE::multi_draw::string() << "is supported, using fallback implementation");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_INFO(Extensions::WEBGL::multi_draw::string() << "is not supported, using fallback implementation");
#endif
#endif
const struct {
Vector2 position;
Vector4 value;
} vertexData[] {
{}, /* initial offset */
{{-1.0f/3.0f, -1.0f/3.0f}, data.values[0]},
{{ 1.0f/3.0f, -1.0f/3.0f}, data.values[1]},
{{-1.0f/3.0f, 1.0f/3.0f}, data.values[2]},
{{ 1.0f/3.0f, 1.0f/3.0f}, data.values[3]},
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawShader::Position{}, MultiDrawShader::Value{});
MAGNUM_VERIFY_NO_GL_ERROR();
/* The signature accepted by glMultiDrawArraysIndirect() */
struct Command {
UnsignedInt count;
UnsignedInt instanceCount;
UnsignedInt first;
UnsignedInt baseInstance;
} commands[] {
{data.counts[0], 0, data.vertexOffsets[0], 0},
{data.counts[1], 0, data.vertexOffsets[1], 0},
{data.counts[2], 0, data.vertexOffsets[2], 0},
{data.counts[3], 0, data.vertexOffsets[3], 0},
};
MultiDrawChecker checker;
MultiDrawShader{data.vertexId, data.drawId}.draw(mesh,
Containers::stridedArrayView(commands).slice(&Command::count),
Containers::stridedArrayView(commands).slice(&Command::first),
nullptr);
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::multiDrawViews() {
auto&& data = MultiDrawData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
if(data.drawId) {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::shader_draw_parameters>())
CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported.");
#elif !defined(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
}
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::EXT::multi_draw_arrays>() &&
!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_INFO("Neither" << Extensions::EXT::multi_draw_arrays::string() << "nor" << Extensions::ANGLE::multi_draw::string() << "is supported, using fallback implementation");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_INFO(Extensions::WEBGL::multi_draw::string() << "is not supported, using fallback implementation");
#endif
#endif
const struct {
Vector2 position;
Vector4 value;
} vertexData[] {
{}, /* initial offset */
{{-1.0f/3.0f, -1.0f/3.0f}, data.values[0]},
{{ 1.0f/3.0f, -1.0f/3.0f}, data.values[1]},
{{-1.0f/3.0f, 1.0f/3.0f}, data.values[2]},
{{ 1.0f/3.0f, 1.0f/3.0f}, data.values[3]},
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawShader::Position{}, MultiDrawShader::Value{});
MeshView a{mesh}, b{mesh}, c{mesh}, d{mesh};
a.setCount(data.counts[0])
.setBaseVertex(data.vertexOffsets[0]);
@ -3585,7 +3748,76 @@ void MeshGLTest::multiDrawIndexed() {
CORRADE_SKIP("gl_VertexID not supported");
#endif
if(data.vertexOffsets[0] || data.vertexOffsets[1] || data.vertexOffsets[2] || data.vertexOffsets[3]) {
bool hasBaseVertex = data.vertexOffsets[0] || data.vertexOffsets[1] || data.vertexOffsets[2] || data.vertexOffsets[3];
if(hasBaseVertex) {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() << "is not supported.");
#elif !defined(MAGNUM_TARGET_WEBGL)
if(!Context::current().isExtensionSupported<Extensions::OES::draw_elements_base_vertex>() &&
!Context::current().isExtensionSupported<Extensions::EXT::draw_elements_base_vertex>())
CORRADE_SKIP(std::string{"Neither "} + Extensions::OES::draw_elements_base_vertex::string() + " nor " + Extensions::EXT::draw_elements_base_vertex::string() + " is available.");
#elif !defined(MAGNUM_TARGET_GLES2)
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() + std::string{" is not available."});
#else
CORRADE_FAIL_IF(false, "Can't do base vertex here.");
#endif
}
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::EXT::multi_draw_arrays>() &&
!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_INFO("Neither" << Extensions::EXT::multi_draw_arrays::string() << "nor" << Extensions::ANGLE::multi_draw::string() << "is supported, using fallback implementation");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_INFO(Extensions::WEBGL::multi_draw::string() << "is not supported, using fallback implementation");
#endif
#endif
const struct {
Vector2 position;
Vector4 value;
} vertexData[] {
{}, /* initial offset */
{{-1.0f/3.0f, -1.0f/3.0f}, data.values[0]},
{{ 1.0f/3.0f, -1.0f/3.0f}, data.values[1]},
{{-1.0f/3.0f, 1.0f/3.0f}, data.values[2]},
{{ 1.0f/3.0f, 1.0f/3.0f}, data.values[3]},
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawShader::Position{}, MultiDrawShader::Value{})
.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, data.indices}, 0, MeshIndexType::UnsignedInt);
MAGNUM_VERIFY_NO_GL_ERROR();
MultiDrawChecker checker;
MultiDrawShader{data.vertexId, false}.draw(mesh, data.counts, hasBaseVertex ? Containers::arrayView(data.vertexOffsets) : nullptr, data.indexOffsetsInBytes);
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::multiDrawIndexedSparseArrays() {
auto&& data = MultiDrawIndexedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#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] || data.vertexOffsets[2] || data.vertexOffsets[3];
if(hasBaseVertex) {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() << "is not supported.");
@ -3593,9 +3825,96 @@ void MeshGLTest::multiDrawIndexed() {
if(!Context::current().isExtensionSupported<Extensions::OES::draw_elements_base_vertex>() &&
!Context::current().isExtensionSupported<Extensions::EXT::draw_elements_base_vertex>())
CORRADE_SKIP(std::string{"Neither "} + Extensions::OES::draw_elements_base_vertex::string() + " nor " + Extensions::EXT::draw_elements_base_vertex::string() + " is available.");
#elif !defined(MAGNUM_TARGET_GLES2)
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() + std::string{" is not available."});
#else
CORRADE_FAIL_IF(false, "Can't do base vertex here.");
#endif
}
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::EXT::multi_draw_arrays>() &&
!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_INFO("Neither" << Extensions::EXT::multi_draw_arrays::string() << "nor" << Extensions::ANGLE::multi_draw::string() << "is supported, using fallback implementation");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_INFO(Extensions::WEBGL::multi_draw::string() << "is not supported, using fallback implementation");
#endif
#endif
const struct {
Vector2 position;
Vector4 value;
} vertexData[] {
{}, /* initial offset */
{{-1.0f/3.0f, -1.0f/3.0f}, data.values[0]},
{{ 1.0f/3.0f, -1.0f/3.0f}, data.values[1]},
{{-1.0f/3.0f, 1.0f/3.0f}, data.values[2]},
{{ 1.0f/3.0f, 1.0f/3.0f}, data.values[3]},
};
Mesh mesh{MeshPrimitive::Points};
mesh.addVertexBuffer(Buffer{vertexData}, sizeof(vertexData[0]), MultiDrawShader::Position{}, MultiDrawShader::Value{})
.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, data.indices}, 0, MeshIndexType::UnsignedInt);
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;
UnsignedInt firstIndexInBytes; /* !! */
UnsignedInt baseVertex;
UnsignedInt baseInstance;
} commands[] {
{data.counts[0], 0, UnsignedInt(data.indexOffsetsInBytes[0]), data.vertexOffsets[0], 0},
{data.counts[1], 0, UnsignedInt(data.indexOffsetsInBytes[1]), data.vertexOffsets[1], 0},
{data.counts[2], 0, UnsignedInt(data.indexOffsetsInBytes[2]), data.vertexOffsets[2], 0},
{data.counts[3], 0, UnsignedInt(data.indexOffsetsInBytes[3]), data.vertexOffsets[3], 0}
};
MultiDrawChecker checker;
MultiDrawShader{data.vertexId, false}.draw(mesh,
Containers::stridedArrayView(commands).slice(&Command::count),
hasBaseVertex ? Containers::stridedArrayView(commands).slice(&Command::baseVertex) : nullptr,
Containers::stridedArrayView(commands).slice(&Command::firstIndexInBytes));
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::multiDrawIndexedViews() {
auto&& data = MultiDrawIndexedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_GLES2
if(data.vertexId && !GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>())
CORRADE_SKIP("gl_VertexID not supported");
#endif
if(data.vertexOffsets[0] || data.vertexOffsets[1] || data.vertexOffsets[2] || data.vertexOffsets[3]) {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::ARB::draw_elements_base_vertex::string() << "is not supported.");
#elif !defined(MAGNUM_TARGET_WEBGL)
if(!Context::current().isExtensionSupported<Extensions::OES::draw_elements_base_vertex>() &&
!Context::current().isExtensionSupported<Extensions::EXT::draw_elements_base_vertex>())
CORRADE_SKIP(std::string{"Neither "} + Extensions::OES::draw_elements_base_vertex::string() + " nor " + Extensions::EXT::draw_elements_base_vertex::string() + " is available.");
#elif !defined(MAGNUM_TARGET_GLES2)
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() + std::string{" is not available."});
#else
CORRADE_FAIL_IF(false, "Can't do base vertex here.");
#endif
}
@ -3627,16 +3946,16 @@ void MeshGLTest::multiDrawIndexed() {
MeshView a{mesh}, b{mesh}, c{mesh}, d{mesh};
a.setCount(data.counts[0])
.setIndexRange(data.indexOffsets[0])
.setIndexRange(data.indexOffsetsInBytes[0]/sizeof(UnsignedInt))
.setBaseVertex(data.vertexOffsets[0]);
b.setCount(data.counts[1])
.setIndexRange(data.indexOffsets[1])
.setIndexRange(data.indexOffsetsInBytes[1]/sizeof(UnsignedInt))
.setBaseVertex(data.vertexOffsets[1]);
c.setCount(data.counts[2])
.setIndexRange(data.indexOffsets[2])
.setIndexRange(data.indexOffsetsInBytes[2]/sizeof(UnsignedInt))
.setBaseVertex(data.vertexOffsets[2]);
d.setCount(data.counts[3])
.setIndexRange(data.indexOffsets[3])
.setIndexRange(data.indexOffsetsInBytes[3]/sizeof(UnsignedInt))
.setBaseVertex(data.vertexOffsets[3]);
MAGNUM_VERIFY_NO_GL_ERROR();
@ -3655,6 +3974,64 @@ void MeshGLTest::multiDrawIndexed() {
#endif
}
void MeshGLTest::multiDrawWrongVertexOffsetSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Mesh mesh;
MultiDrawShader shader;
UnsignedInt counts[3]{};
UnsignedInt vertexOffsets[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, vertexOffsets, nullptr);
shader.draw(mesh, counts, 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");
}
void MeshGLTest::multiDrawIndexedWrongIndexOffsetSize() {
#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);
MultiDrawShader shader;
UnsignedInt counts[3]{};
UnsignedInt indexOffsets[2]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, nullptr, indexOffsets);
shader.draw(mesh, counts, 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");
}
void MeshGLTest::multiDrawIndexedWrongVertexOffsetSize() {
#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);
MultiDrawShader shader;
UnsignedInt counts[3]{};
UnsignedInt vertexOffsets[2]{};
UnsignedInt indexOffsets[3]{};
std::ostringstream out;
Error redirectError{&out};
shader.draw(mesh, counts, vertexOffsets, indexOffsets);
CORRADE_COMPARE(out.str(),
"GL::AbstractShaderProgram::draw(): expected 3 vertex offset items but got 2\n");
}
#ifdef MAGNUM_TARGET_GLES
void MeshGLTest::multiDrawIndexedBaseVertexNoExtensionAvailable() {
#ifdef MAGNUM_TARGET_GLES
@ -3680,12 +4057,49 @@ void MeshGLTest::multiDrawIndexedBaseVertexNoExtensionAvailable() {
CORRADE_SKIP(Extensions::WEBGL::multi_draw_instanced_base_vertex_base_instance::string() + std::string{" is available."});
#endif
constexpr UnsignedShort indexData[] = { 2, 1, 0 };
Buffer indices{Buffer::TargetHint::ElementArray};
indices.setData(indexData, BufferUsage::StaticDraw);
Mesh mesh;
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
UnsignedInt counts[]{3};
UnsignedInt vertexOffsets[]{0};
UnsignedInt indexOffsets[]{0};
std::ostringstream out;
Error redirectError{&out};
MultiDrawShader{}.draw(mesh, counts, vertexOffsets, indexOffsets);
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): no extension available for indexed mesh multi-draw with base vertex specification\n");
#else
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): indexed mesh multi-draw with base vertex specification possible only since WebGL 2.0\n");
#endif
}
void MeshGLTest::multiDrawIndexedViewsBaseVertexNoExtensionAvailable() {
#ifdef MAGNUM_TARGET_GLES
/* If the multidraw extensions aren't available, we can't test this assert,
only the assert in the fallback path, which is already tested above. */
#ifndef MAGNUM_TARGET_WEBGL
if(!Context::current().isExtensionSupported<Extensions::EXT::multi_draw_arrays>() &&
!Context::current().isExtensionSupported<Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(std::string{"Neither "} + Extensions::EXT::multi_draw_arrays::string() + " nor " + Extensions::ANGLE::multi_draw::string() + " is available.");
#else
if(!Context::current().isExtensionSupported<Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(Extensions::WEBGL::multi_draw::string() + std::string{" is not available."});
#endif
#endif
#ifndef MAGNUM_TARGET_WEBGL
if(Context::current().isExtensionSupported<Extensions::EXT::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::EXT::draw_elements_base_vertex::string() + std::string{" is available."});
if(Context::current().isExtensionSupported<Extensions::OES::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::OES::draw_elements_base_vertex::string() + std::string{" is available."});
#elif !defined(MAGNUM_TARGET_GLES2)
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() + std::string{" is available."});
#endif
Mesh mesh;
mesh.setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort);
mesh.setIndexBuffer(Buffer{Buffer::TargetHint::ElementArray, {2, 1, 0}}, 0, MeshIndexType::UnsignedInt);
MeshView view{mesh};
view.setCount(3)
@ -3702,7 +4116,7 @@ void MeshGLTest::multiDrawIndexedBaseVertexNoExtensionAvailable() {
}
#endif
void MeshGLTest::multiDrawInstanced() {
void MeshGLTest::multiDrawInstancedViews() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
@ -3718,7 +4132,7 @@ void MeshGLTest::multiDrawInstanced() {
CORRADE_COMPARE(out.str(), "GL::AbstractShaderProgram::draw(): cannot multi-draw instanced meshes\n");
}
void MeshGLTest::multiDrawDifferentMeshes() {
void MeshGLTest::multiDrawViewsDifferentMeshes() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif

11
src/Magnum/Shaders/DistanceFieldVectorGL.h

@ -580,6 +580,17 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT DistanceFieldVector
DistanceFieldVectorGL<dimensions>& draw(GL::MeshView&& mesh) {
return static_cast<DistanceFieldVectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh));
}
DistanceFieldVectorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<DistanceFieldVectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
DistanceFieldVectorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<DistanceFieldVectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
DistanceFieldVectorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<DistanceFieldVectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
DistanceFieldVectorGL<dimensions>& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<DistanceFieldVectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(meshes));
}

11
src/Magnum/Shaders/FlatGL.h

@ -911,6 +911,17 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
FlatGL<dimensions>& draw(GL::MeshView&& mesh) {
return static_cast<FlatGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh));
}
FlatGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<FlatGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
FlatGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<FlatGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
FlatGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<FlatGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
FlatGL<dimensions>& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<FlatGL<dimensions>&>(GL::AbstractShaderProgram::draw(meshes));
}

22
src/Magnum/Shaders/MeshVisualizerGL.h

@ -611,6 +611,17 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
MeshVisualizerGL2D& draw(GL::MeshView&& mesh) {
return static_cast<MeshVisualizerGL2D&>(GL::AbstractShaderProgram::draw(mesh));
}
MeshVisualizerGL2D& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<MeshVisualizerGL2D&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
MeshVisualizerGL2D& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<MeshVisualizerGL2D&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
MeshVisualizerGL2D& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<MeshVisualizerGL2D&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
MeshVisualizerGL2D& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<MeshVisualizerGL2D&>(GL::AbstractShaderProgram::draw(meshes));
}
@ -1644,6 +1655,17 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
MeshVisualizerGL3D& draw(GL::MeshView&& mesh) {
return static_cast<MeshVisualizerGL3D&>(GL::AbstractShaderProgram::draw(mesh));
}
MeshVisualizerGL3D& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<MeshVisualizerGL3D&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
MeshVisualizerGL3D& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<MeshVisualizerGL3D&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
MeshVisualizerGL3D& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<MeshVisualizerGL3D&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
MeshVisualizerGL3D& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<MeshVisualizerGL3D&>(GL::AbstractShaderProgram::draw(meshes));
}

11
src/Magnum/Shaders/PhongGL.h

@ -1645,6 +1645,17 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
PhongGL& draw(GL::MeshView&& mesh) {
return static_cast<PhongGL&>(GL::AbstractShaderProgram::draw(mesh));
}
PhongGL& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<PhongGL&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
PhongGL& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<PhongGL&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
PhongGL& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<PhongGL&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
PhongGL& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<PhongGL&>(GL::AbstractShaderProgram::draw(meshes));
}

11
src/Magnum/Shaders/VectorGL.h

@ -532,6 +532,17 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT VectorGL: public GL
VectorGL<dimensions>& draw(GL::MeshView&& mesh) {
return static_cast<VectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh));
}
VectorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<VectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
VectorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<VectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
VectorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<VectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
VectorGL<dimensions>& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<VectorGL<dimensions>&>(GL::AbstractShaderProgram::draw(meshes));
}

11
src/Magnum/Shaders/VertexColorGL.h

@ -384,6 +384,17 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT VertexColorGL: publ
VertexColorGL<dimensions>& draw(GL::MeshView&& mesh) {
return static_cast<VertexColorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh));
}
VertexColorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedInt>& indexOffsets) {
return static_cast<VertexColorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
#ifndef CORRADE_TARGET_32BIT
VertexColorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, const Containers::StridedArrayView1D<const UnsignedLong>& indexOffsets) {
return static_cast<VertexColorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets));
}
VertexColorGL<dimensions>& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D<const UnsignedInt>& counts, const Containers::StridedArrayView1D<const UnsignedInt>& vertexOffsets, std::nullptr_t) {
return static_cast<VertexColorGL<dimensions>&>(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr));
}
#endif
VertexColorGL<dimensions>& draw(Containers::ArrayView<const Containers::Reference<GL::MeshView>> meshes) {
return static_cast<VertexColorGL<dimensions>&>(GL::AbstractShaderProgram::draw(meshes));
}

Loading…
Cancel
Save