Browse Source

Added support for glMultiDrawArrays()/glMultiDrawElements().

In OpenGL ES this is implemented in EXT_multi_draw_arrays extension, if
it is not available, the functionality is emulated using sequence of
normal draw() calls.
pull/59/head
Vladimír Vondruš 12 years ago
parent
commit
5998f46e12
  1. 3
      doc/opengl-mapping.dox
  2. 3
      doc/opengl-support.dox
  3. 1
      src/Magnum/Context.cpp
  4. 1
      src/Magnum/Extensions.h
  5. 10
      src/Magnum/Implementation/MeshState.cpp
  6. 4
      src/Magnum/Implementation/MeshState.h
  7. 1
      src/Magnum/Implementation/setupDriverWorkarounds.cpp
  8. 13
      src/Magnum/Mesh.h
  9. 105
      src/Magnum/MeshView.cpp
  10. 33
      src/Magnum/MeshView.h
  11. 139
      src/Magnum/Test/MeshGLTest.cpp

3
doc/opengl-mapping.dox

@ -196,8 +196,7 @@ OpenGL function | Matching API
@fn_gl_extension{MapBufferSubData,CHROMIUM,map_sub}, @fn_gl_extension{UnmapBufferSubData,CHROMIUM,map_sub} | @ref Buffer::mapSub(), @ref Buffer::unmapSub()
@fn_gl{MemoryBarrier} | |
@fn_gl{MinSampleShading} | |
@fn_gl{MultiDrawArrays}, \n @fn_gl{MultiDrawElements} | |
@fn_gl{MultiDrawElementsBaseVertex} | |
@fn_gl{MultiDrawArrays}, \n @fn_gl{MultiDrawElements}, \n @fn_gl{MultiDrawElementsBaseVertex} | @ref MeshView::draw(AbstractShaderProgram&, std::initializer_list<std::reference_wrapper<MeshView>>)
@fn_gl{ObjectLabel}, \n @fn_gl{ObjectPtrLabel}, \n @fn_gl_extension2{LabelObject,EXT,debug_label} | @ref AbstractShaderProgram::setLabel(), \n @ref AbstractQuery::setLabel(), \n @ref AbstractTexture::setLabel(), \n @ref Buffer::setLabel(), \n @ref Framebuffer::setLabel(), \n @ref Mesh::setLabel(), \n @ref Renderbuffer::setLabel(), \n @ref Shader::setLabel()
@fn_gl{PatchParameter} | |
@fn_gl{PauseTransformFeedback}, @fn_gl{ResumeTransformFeedback} | |

3
doc/opengl-support.dox

@ -95,7 +95,7 @@ following:
-------------------------------------------- | ------
@extension{ARB,geometry_shader4} | missing layered attachments
@extension{ARB,depth_clamp} | done
@extension{ARB,draw_elements_base_vertex} | missing `Multi*` command
@extension{ARB,draw_elements_base_vertex} | done
@extension{ARB,fragment_coord_conventions} | done (shading language only)
@extension{ARB,provoking_vertex} | done
@extension{ARB,seamless_cube_map} | done
@ -299,6 +299,7 @@ Only extensions not already listed in above tables are included here.
@es_extension{CHROMIUM,map_sub} | only buffer mapping
@es_extension{EXT,texture_format_BGRA8888} | done
@es_extension{EXT,read_format_bgra} | done
@es_extension2{EXT,multi_draw_arrays,multi_draw_arrays} | done
@es_extension{EXT,disjoint_timer_query} | only time elapsed query
@es_extension{EXT,separate_shader_objects} | only direct uniform binding
@es_extension{EXT,sRGB} | done

1
src/Magnum/Context.cpp

@ -197,6 +197,7 @@ const std::vector<Extension>& Extension::extensions(Version version) {
_extension(GL,EXT,texture_filter_anisotropic),
_extension(GL,EXT,texture_format_BGRA8888),
_extension(GL,EXT,read_format_bgra),
_extension(GL,EXT,multi_draw_arrays),
_extension(GL,EXT,debug_label),
_extension(GL,EXT,debug_marker),
_extension(GL,EXT,disjoint_timer_query),

1
src/Magnum/Extensions.h

@ -239,6 +239,7 @@ namespace GL {
_extension(GL,EXT,blend_minmax, GLES200, GLES300) // #65
#endif
_extension(GL,EXT,read_format_bgra, GLES200, None) // #66
_extension(GL,EXT,multi_draw_arrays, GLES200, None) // #67
#ifdef MAGNUM_TARGET_GLES2
_extension(GL,EXT,shader_texture_lod, GLES200, GLES300) // #77
#endif

10
src/Magnum/Implementation/MeshState.cpp

@ -27,6 +27,7 @@
#include "Magnum/Context.h"
#include "Magnum/Extensions.h"
#include "Magnum/MeshView.h"
#include "State.h"
@ -95,6 +96,15 @@ MeshState::MeshState(Context& context, std::vector<std::string>& extensions): cu
}
#endif
#ifdef MAGNUM_TARGET_GLES
/* Multi draw implementation on ES */
if(context.isExtensionSupported<Extensions::GL::EXT::multi_draw_arrays>()) {
extensions.push_back(Extensions::GL::EXT::multi_draw_arrays::string());
multiDrawImplementation = &MeshView::multiDrawImplementationDefault;
} else multiDrawImplementation = &MeshView::multiDrawImplementationFallback;
#endif
#ifdef MAGNUM_TARGET_GLES2
/* Instanced draw ímplementation on ES2 */
if(context.isExtensionSupported<Extensions::GL::ANGLE::instanced_arrays>()) {

4
src/Magnum/Implementation/MeshState.h

@ -58,6 +58,10 @@ struct MeshState {
void(Mesh::*drawElementsInstancedImplementation)(GLsizei, GLintptr, GLsizei);
#endif
#ifdef MAGNUM_TARGET_GLES
void(*multiDrawImplementation)(std::initializer_list<std::reference_wrapper<MeshView>>);
#endif
GLuint currentVAO;
#ifndef MAGNUM_TARGET_GLES2
GLint maxElementsIndices, maxElementsVertices;

1
src/Magnum/Implementation/setupDriverWorkarounds.cpp

@ -56,6 +56,7 @@ void Context::setupDriverWorkarounds() {
#ifndef CORRADE_TARGET_NACL
_setRequiredVersion(GL::CHROMIUM::map_sub, None);
#endif
_setRequiredVersion(GL::EXT::multi_draw_arrays, None);
_setRequiredVersion(GL::EXT::debug_label, None);
_setRequiredVersion(GL::EXT::debug_marker, None);
_setRequiredVersion(GL::EXT::disjoint_timer_query, None);

13
src/Magnum/Mesh.h

@ -750,11 +750,14 @@ class MAGNUM_EXPORT Mesh: public AbstractObject {
* commands are issued. See also
* @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation"
* for more information.
* @see @ref setCount(), @ref setInstanceCount(), @fn_gl{UseProgram},
* @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl{VertexAttribPointer}, @fn_gl{DisableVertexAttribArray}
* or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object}
* is available), @fn_gl{DrawArrays}/@fn_gl{DrawArraysInstanced}/
* @see @ref setCount(), @ref setInstanceCount(),
* @ref MeshView::draw(AbstractShaderProgram&),
* @ref MeshView::draw(AbstractShaderProgram&, std::initializer_list<std::reference_wrapper<MeshView>>),
* @fn_gl{UseProgram}, @fn_gl{EnableVertexAttribArray},
* @fn_gl{BindBuffer}, @fn_gl{VertexAttribPointer},
* @fn_gl{DisableVertexAttribArray} or @fn_gl{BindVertexArray} (if
* @extension{APPLE,vertex_array_object} is available),
* @fn_gl{DrawArrays}/@fn_gl{DrawArraysInstanced}/
* @fn_gl{DrawArraysInstancedBaseInstance} or @fn_gl{DrawElements}/
* @fn_gl{DrawRangeElements}/@fn_gl{DrawElementsBaseVertex}/
* @fn_gl{DrawRangeElementsBaseVertex}/@fn_gl{DrawElementsInstanced}/

105
src/Magnum/MeshView.cpp

@ -25,10 +25,115 @@
#include "MeshView.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Utility/Assert.h>
#include "Magnum/Context.h"
#include "Magnum/Mesh.h"
#include "Implementation/State.h"
#include "Implementation/MeshState.h"
namespace Magnum {
void MeshView::draw(AbstractShaderProgram& shader, std::initializer_list<std::reference_wrapper<MeshView>> meshes) {
/* Why std::initializer_list doesn't have empty()? */
if(!meshes.size()) return;
shader.use();
#ifndef CORRADE_NO_ASSERT
const Mesh* original = meshes.begin()->get()._original;
for(MeshView& mesh: meshes)
CORRADE_ASSERT(mesh._original == original, "MeshView::draw(): all meshes must be views of the same original mesh", );
#endif
#ifndef MAGNUM_TARGET_GLES
multiDrawImplementationDefault(meshes);
#else
Context::current()->state().mesh->multiDrawImplementation(meshes);
#endif
}
void MeshView::multiDrawImplementationDefault(std::initializer_list<std::reference_wrapper<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()};
/* Gather the parameters */
#ifndef MAGNUM_TARGET_GLES
bool hasBaseVertex = false;
#endif
std::size_t i = 0;
for(MeshView& mesh: meshes) {
CORRADE_ASSERT(mesh._instanceCount == 1, "MeshView::draw(): cannot draw multiple instanced meshes", );
count[i] = mesh._count;
indices[i] = reinterpret_cast<GLvoid*>(mesh._indexOffset);
baseVertex[i] = mesh._baseVertex;
if(mesh._baseVertex) {
#ifndef MAGNUM_TARGET_GLES
hasBaseVertex = true;
#else
CORRADE_ASSERT(!original._indexBuffer, "MeshView::draw(): desktop OpenGL is required for base vertex specification in indexed meshes", );
#endif
}
++i;
}
(original.*state.bindImplementation)();
/* Non-indexed meshes */
if(!original._indexBuffer) {
#ifndef MAGNUM_TARGET_GLES
glMultiDrawArrays(GLenum(original._primitive), baseVertex, count, meshes.size());
#else
//glMultiDrawArraysEXT(GLenum(original._primitive), baseVertex, count, meshes.size());
CORRADE_INTERNAL_ASSERT(false);
#endif
/* Indexed meshes */
} else {
/* Indexed meshes with base vertex */
#ifndef MAGNUM_TARGET_GLES
if(hasBaseVertex) {
glMultiDrawElementsBaseVertex(GLenum(original._primitive), count, GLenum(original._indexType), indices, meshes.size(), baseVertex);
/* Indexed meshes */
} else
#endif
{
#ifndef MAGNUM_TARGET_GLES
glMultiDrawElements(GLenum(original._primitive), count, GLenum(original._indexType), indices, meshes.size());
#else
//glMultiDrawElements(GLenum(original._primitive), count, GLenum(original._indexType), indices, meshes.size());
CORRADE_INTERNAL_ASSERT(false);
#endif
}
}
(original.*state.unbindImplementation)();
}
#ifdef MAGNUM_TARGET_GLES
void MeshView::multiDrawImplementationFallback(std::initializer_list<std::reference_wrapper<MeshView>> meshes) {
for(MeshView& mesh: meshes) {
#ifndef MAGNUM_TARGET_GLES2
mesh._original->drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset, mesh._indexStart, mesh._indexEnd);
#else
mesh._original->drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset);
#endif
}
}
#endif
MeshView& MeshView::setIndexRange(Int first) {
_indexOffset = _original->_indexOffset + first*_original->indexSize();
return *this;

33
src/Magnum/MeshView.h

@ -29,12 +29,17 @@
* @brief Class @ref Magnum::MeshView
*/
#include <functional>
#include <initializer_list>
#include "Magnum/Magnum.h"
#include "Magnum/OpenGL.h"
#include "Magnum/visibility.h"
namespace Magnum {
namespace Implementation { struct MeshState; }
/**
@brief %Mesh view
@ -52,7 +57,31 @@ You must ensure that the original mesh remains available for whole view
lifetime.
*/
class MAGNUM_EXPORT MeshView {
friend struct Implementation::MeshState;
public:
/**
* @brief Draw multiple meshes at once
*
* In OpenGL ES, if @es_extension2{EXT,multi_draw_arrays,multi_draw_arrays}
* is not present, the functionality is emulated using sequence of
* @ref draw(AbstractShaderProgram&) calls.
* @attention All meshes must be views of the same original mesh and
* must not be instanced.
* @see @ref draw(AbstractShaderProgram&), @fn_gl{UseProgram},
* @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl{VertexAttribPointer}, @fn_gl{DisableVertexAttribArray}
* or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object}
* is available), @fn_gl{MultiDrawArrays} or
* @fn_gl{MultiDrawElements}/@fn_gl{MultiDrawElementsBaseVertex}
*/
static void draw(AbstractShaderProgram& shader, std::initializer_list<std::reference_wrapper<MeshView>> meshes);
/** @overload */
static void draw(AbstractShaderProgram&& shader, std::initializer_list<std::reference_wrapper<MeshView>> meshes) {
draw(shader, meshes);
}
/**
* @brief Constructor
* @param original Original, already configured mesh
@ -212,6 +241,7 @@ class MAGNUM_EXPORT MeshView {
* @brief Draw the mesh
*
* See @ref Mesh::draw() for more information.
* @see @ref draw(AbstractShaderProgram&, std::initializer_list<std::reference_wrapper<MeshView>>)
*/
void draw(AbstractShaderProgram& shader);
void draw(AbstractShaderProgram&& shader) { draw(shader); } /**< @overload */
@ -226,6 +256,9 @@ class MAGNUM_EXPORT MeshView {
#endif
private:
static MAGNUM_LOCAL void multiDrawImplementationDefault(std::initializer_list<std::reference_wrapper<MeshView>> meshes);
static MAGNUM_LOCAL void multiDrawImplementationFallback(std::initializer_list<std::reference_wrapper<MeshView>> meshes);
Mesh* _original;
Int _count, _baseVertex, _instanceCount;

139
src/Magnum/Test/MeshGLTest.cpp

@ -126,6 +126,12 @@ class MeshGLTest: public AbstractOpenGLTester {
#ifndef MAGNUM_TARGET_GLES
void addVertexBufferInstancedDouble();
#endif
void multiDraw();
void multiDrawIndexed();
#ifndef MAGNUM_TARGET_GLES
void multiDrawBaseVertex();
#endif
};
MeshGLTest::MeshGLTest() {
@ -207,7 +213,13 @@ MeshGLTest::MeshGLTest() {
&MeshGLTest::addVertexBufferInstancedInteger,
#endif
#ifndef MAGNUM_TARGET_GLES
&MeshGLTest::addVertexBufferInstancedDouble
&MeshGLTest::addVertexBufferInstancedDouble,
#endif
&MeshGLTest::multiDraw,
&MeshGLTest::multiDrawIndexed,
#ifndef MAGNUM_TARGET_GLES
&MeshGLTest::multiDrawBaseVertex
#endif
});
}
@ -1662,6 +1674,131 @@ void MeshGLTest::addVertexBufferInstancedDouble() {
}
#endif
namespace {
struct MultiChecker {
MultiChecker(AbstractShaderProgram&& shader, Mesh& mesh);
template<class T> T get(ColorFormat format, ColorType type);
Renderbuffer renderbuffer;
Framebuffer framebuffer;
};
}
#ifndef DOXYGEN_GENERATING_OUTPUT
MultiChecker::MultiChecker(AbstractShaderProgram&& shader, Mesh& mesh): framebuffer({{}, Vector2i(1)}) {
renderbuffer.setStorage(
#ifndef MAGNUM_TARGET_GLES2
RenderbufferFormat::RGBA8,
#else
RenderbufferFormat::RGBA4,
#endif
Vector2i(1));
framebuffer.attachRenderbuffer(Framebuffer::ColorAttachment(0), renderbuffer);
framebuffer.bind(FramebufferTarget::ReadDraw);
mesh.setPrimitive(MeshPrimitive::Points)
.setCount(2);
/* Skip first vertex so we test also offsets */
MeshView a(mesh);
a.setCount(1)
.setBaseVertex(mesh.baseVertex());
MeshView b(mesh);
b.setCount(1);
if(mesh.isIndexed()) {
b.setBaseVertex(mesh.baseVertex())
.setIndexRange(1);
} else b.setBaseVertex(1);
MeshView::draw(shader, {a, b});
}
template<class T> T MultiChecker::get(ColorFormat format, ColorType type) {
Image2D image(format, type);
framebuffer.read({}, Vector2i(1), image);
return image.data<T>()[0];
}
#endif
void MeshGLTest::multiDraw() {
#ifdef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::multi_draw_arrays>())
Debug() << Extensions::GL::EXT::multi_draw_arrays::string() << "not supported, using fallback implementation";
#endif
typedef AbstractShaderProgram::Attribute<0, Float> Attribute;
const Float data[] = { 0.0f, -0.7f, Math::normalize<Float, UnsignedByte>(96) };
Buffer buffer;
buffer.setData(data, BufferUsage::StaticDraw);
Mesh mesh;
mesh.addVertexBuffer(buffer, 4, Attribute());
MAGNUM_VERIFY_NO_ERROR();
const auto value = MultiChecker(FloatShader("float", "vec4(valueInterpolated, 0.0, 0.0, 0.0)"),
mesh).get<UnsignedByte>(ColorFormat::RGBA, ColorType::UnsignedByte);
MAGNUM_VERIFY_NO_ERROR();
CORRADE_COMPARE(value, 96);
}
void MeshGLTest::multiDrawIndexed() {
#ifdef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::multi_draw_arrays>())
Debug() << Extensions::GL::EXT::multi_draw_arrays::string() << "not supported, using fallback implementation";
#endif
Buffer vertices;
vertices.setData(indexedVertexData, BufferUsage::StaticDraw);
constexpr UnsignedShort indexData[] = { 2, 1, 0 };
Buffer indices(Buffer::Target::ElementArray);
indices.setData(indexData, BufferUsage::StaticDraw);
Mesh mesh;
mesh.addVertexBuffer(vertices, 1*4, MultipleShader::Position(),
MultipleShader::Normal(), MultipleShader::TextureCoordinates())
.setIndexBuffer(indices, 2, Mesh::IndexType::UnsignedShort);
MAGNUM_VERIFY_NO_ERROR();
const auto value = MultiChecker(MultipleShader{}, mesh).get<Color4ub>(ColorFormat::RGBA, ColorType::UnsignedByte);
MAGNUM_VERIFY_NO_ERROR();
CORRADE_COMPARE(value, indexedResult);
}
#ifndef MAGNUM_TARGET_GLES
void MeshGLTest::multiDrawBaseVertex() {
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::draw_elements_base_vertex>())
CORRADE_SKIP(Extensions::GL::ARB::draw_elements_base_vertex::string() + std::string(" is not available."));
Buffer vertices;
vertices.setData(indexedVertexDataBaseVertex, BufferUsage::StaticDraw);
constexpr UnsignedShort indexData[] = { 2, 1, 0 };
Buffer indices(Buffer::Target::ElementArray);
indices.setData(indexData, BufferUsage::StaticDraw);
Mesh mesh;
mesh.setBaseVertex(2)
.addVertexBuffer(vertices, 2*4, MultipleShader::Position(),
MultipleShader::Normal(), MultipleShader::TextureCoordinates())
.setIndexBuffer(indices, 2, Mesh::IndexType::UnsignedShort);
MAGNUM_VERIFY_NO_ERROR();
const auto value = MultiChecker(MultipleShader{}, mesh).get<Color4ub>(ColorFormat::RGBA, ColorType::UnsignedByte);
MAGNUM_VERIFY_NO_ERROR();
CORRADE_COMPARE(value, indexedResult);
}
#endif
}}
CORRADE_TEST_MAIN(Magnum::Test::MeshGLTest)

Loading…
Cancel
Save