From 2c09a2a1e6456b01b03bc6a68de1cdc26e3508ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 May 2021 22:55:06 +0200 Subject: [PATCH] Shaders: multidraw support in all builtin shaders. --- doc/changelog.dox | 3 +- src/Magnum/Shaders/DistanceFieldVector.frag | 9 +- src/Magnum/Shaders/DistanceFieldVectorGL.cpp | 15 ++ src/Magnum/Shaders/DistanceFieldVectorGL.h | 34 +++- src/Magnum/Shaders/Flat.frag | 13 +- src/Magnum/Shaders/Flat.vert | 28 +++- src/Magnum/Shaders/FlatGL.cpp | 15 ++ src/Magnum/Shaders/FlatGL.h | 34 +++- src/Magnum/Shaders/MeshVisualizer.frag | 9 +- src/Magnum/Shaders/MeshVisualizer.geom | 18 +- src/Magnum/Shaders/MeshVisualizer.vert | 44 ++++- src/Magnum/Shaders/MeshVisualizerGL.cpp | 19 +++ src/Magnum/Shaders/MeshVisualizerGL.h | 65 +++++++- src/Magnum/Shaders/Phong.frag | 15 +- src/Magnum/Shaders/Phong.vert | 34 +++- src/Magnum/Shaders/PhongGL.cpp | 15 ++ src/Magnum/Shaders/PhongGL.h | 31 +++- .../Test/DistanceFieldVectorGLTest.cpp | 118 +++++++++---- .../Test/DistanceFieldVectorGL_Test.cpp | 33 +++- src/Magnum/Shaders/Test/FlatGLTest.cpp | 115 ++++++++++--- src/Magnum/Shaders/Test/FlatGL_Test.cpp | 17 +- .../Shaders/Test/MeshVisualizerGLTest.cpp | 156 +++++++++++++++--- .../Shaders/Test/MeshVisualizerGL_Test.cpp | 50 ++++-- src/Magnum/Shaders/Test/PhongGLTest.cpp | 83 +++++++--- src/Magnum/Shaders/Test/PhongGL_Test.cpp | 17 +- src/Magnum/Shaders/Test/VectorGLTest.cpp | 118 +++++++++---- src/Magnum/Shaders/Test/VectorGL_Test.cpp | 33 +++- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 120 ++++++++++---- .../Shaders/Test/VertexColorGL_Test.cpp | 18 +- src/Magnum/Shaders/Vector.frag | 11 +- src/Magnum/Shaders/Vector.vert | 28 +++- src/Magnum/Shaders/VectorGL.cpp | 15 ++ src/Magnum/Shaders/VectorGL.h | 34 +++- src/Magnum/Shaders/VertexColor.vert | 22 ++- src/Magnum/Shaders/VertexColorGL.cpp | 14 ++ src/Magnum/Shaders/VertexColorGL.h | 34 +++- 36 files changed, 1202 insertions(+), 235 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 99db5d87e..e2fc0271d 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -148,7 +148,8 @@ See also: @subsubsection changelog-latest-new-shaders Shaders library - All builtin shaders now have opt-in support for uniform buffers on desktop, - OpenGL ES 3.0+ and WebGL 2.0 + OpenGL ES 3.0+ and WebGL 2.0, including multi-draw functionality for + massive driver overhead reduction - Added @ref Shaders::PhongGL::setNormalTextureScale(), consuming the recently added @ref Trade::MaterialAttribute::NormalTextureScale material attribute diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 4f292e38f..ab6733f4f 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -71,6 +71,7 @@ uniform lowp float smoothness /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -79,6 +80,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif struct DrawUniform { highp uvec4 materialIdReservedReservedReservedReserved; @@ -122,6 +125,10 @@ uniform lowp sampler2D vectorTexture; in mediump vec2 interpolatedTextureCoordinates; +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* OUtput */ #ifdef NEW_GLSL @@ -133,7 +140,7 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp const float smoothness = materials[materialId].material_smoothness; lowp const vec4 color = materials[materialId].color; lowp const vec4 outlineColor = materials[materialId].outlineColor; diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 4ded3988b..2a9e55175 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -85,6 +85,17 @@ template DistanceFieldVectorGL::DistanceFiel if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -112,6 +123,7 @@ template DistanceFieldVectorGL::DistanceFiel "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -124,6 +136,7 @@ template DistanceFieldVectorGL::DistanceFiel "#define DRAW_COUNT {}\n", materialCount, drawCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(rs.get("generic.glsl")) @@ -351,6 +364,7 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -363,6 +377,7 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::DistanceFieldVectorGL::Flags{}", { DistanceFieldVectorGLFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + DistanceFieldVectorGLFlag::MultiDraw, /* Superset of UniformBuffers */ DistanceFieldVectorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 604c6106b..4284cd7c1 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -41,7 +41,8 @@ namespace Implementation { enum class DistanceFieldVectorGLFlag: UnsignedByte { TextureTransformation = 1 << 0, #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + MultiDraw = UniformBuffers|(1 << 2) #endif }; typedef Containers::EnumSet DistanceFieldVectorGLFlags; @@ -140,7 +141,31 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 2) #endif }; @@ -387,6 +412,11 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @ref bindTextureTransformationBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 613f84b05..b9fa74103 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -75,6 +75,7 @@ uniform highp uint objectId; /* defaults to zero */ /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -83,6 +84,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif struct DrawUniform { lowp vec4 color; @@ -139,14 +142,18 @@ layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION) out highp uint fragmentObjectId; #endif +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS - lowp const vec4 color = draws[drawOffset].color; + lowp const vec4 color = draws[drawId].color; #ifdef OBJECT_ID - highp const uint objectId = draws[drawOffset].draw_objectId; + highp const uint objectId = draws[drawId].draw_objectId; #endif #ifdef ALPHA_MASK - lowp const float alphaMask = uintBitsToFloat(draws[drawOffset].draw_alphaMask); + lowp const float alphaMask = uintBitsToFloat(draws[drawId].draw_alphaMask); #endif #endif diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index 93fa99e1b..0cf02e95d 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -27,6 +27,14 @@ #extension GL_EXT_gpu_shader4: require #endif +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -182,8 +190,24 @@ out lowp vec4 interpolatedVertexColor; flat out highp uint interpolatedInstanceObjectId; #endif +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + highp const #ifdef TWO_DIMENSIONS mat3 @@ -192,9 +216,9 @@ void main() { #else #error #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #ifdef TEXTURE_TRANSFORMATION - mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); #endif #endif diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 5887f781c..c91221fb6 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -84,6 +84,17 @@ template FlatGL::FlatGL(const Flags flags if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -118,6 +129,7 @@ template FlatGL::FlatGL(const Flags flags "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -136,6 +148,7 @@ template FlatGL::FlatGL(const Flags flags "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(rs.get("generic.glsl")) @@ -370,6 +383,7 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) { _c(InstancedTextureOffset) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -391,6 +405,7 @@ Debug& operator<<(Debug& debug, const FlatGLFlags value) { #endif FlatGLFlag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 + FlatGLFlag::MultiDraw, /* Superset of UniformBuffers */ FlatGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index d052ca5e7..1daa1c6c4 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -50,7 +50,8 @@ namespace Implementation { InstancedTransformation = 1 << 6, InstancedTextureOffset = (1 << 7)|TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 8 + UniformBuffers = 1 << 8, + MultiDraw = UniformBuffers|(1 << 9) #endif }; typedef Containers::EnumSet FlatGLFlags; @@ -388,7 +389,31 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 8 + UniformBuffers = 1 << 8, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 9) #endif }; @@ -602,6 +627,11 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref bindTextureTransformationBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index aa4889a92..6987931ba 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -110,6 +110,7 @@ uniform lowp vec2 colorMapOffsetScale /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -118,6 +119,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif /* Keep in sync with MeshVisualizer.vert and MeshVisualizer.geom. Can't "outsource" to a common file because the extension directives need to be @@ -202,6 +205,10 @@ in lowp vec4 backgroundColor; in lowp vec4 lineColor; #endif +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* Outputs */ #ifdef NEW_GLSL @@ -213,7 +220,7 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #if (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) && !defined(TBN_DIRECTION) lowp const vec4 color = materials[materialId].color; lowp const vec4 wireframeColor = materials[materialId].wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index 89709595f..3cfe14402 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -87,6 +87,7 @@ uniform lowp float smoothness /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -95,6 +96,7 @@ uniform highp uint drawOffset = 0u #endif ; +#endif /* Keep in sync with MeshVisualizer.vert and MeshVisualizer.frag. Can't "outsource" to a common file because the #extension directives need to be @@ -166,6 +168,10 @@ in highp float interpolatedVsMappedVertexId[]; flat in highp uint interpolatedVsPrimitiveId[]; #endif +#ifdef MULTI_DRAW +flat in highp uint vsDrawId[]; +#endif + /* Outputs */ layout(triangle_strip, max_vertices = MAX_VERTICES) out; @@ -186,6 +192,10 @@ out highp float interpolatedMappedVertexId; flat out highp uint interpolatedPrimitiveId; #endif +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) out lowp vec4 backgroundColor; out lowp vec4 lineColor; @@ -255,7 +265,13 @@ void emitQuad( void main() { #ifdef UNIFORM_BUFFERS - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + #ifdef MULTI_DRAW + drawId = vsDrawId[0]; + #else + #define drawId drawOffset + #endif + + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) lowp const vec4 color = materials[materialId].color; lowp const vec4 wireframeColor = materials[materialId].wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 38ce85ffb..6aa9ebe7b 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -27,6 +27,14 @@ #extension GL_EXT_gpu_shader4: require #endif +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -279,19 +287,47 @@ out highp vec4 bitangentEndpoint; out highp vec4 normalEndpoint; #endif +#ifdef MULTI_DRAW +flat out highp uint + #ifdef NO_GEOMETRY_SHADER + drawId + #else + vsDrawId + #endif + ; +#endif + void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + #ifdef NO_GEOMETRY_SHADER + drawId + #else + vsDrawId + #define drawId vsDrawId + #endif + = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + #ifdef TWO_DIMENSIONS - highp const mat3 transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + highp const mat3 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #elif defined(THREE_DIMENSIONS) - highp const mat4 transformationMatrix = transformationMatrices[drawOffset]; + highp const mat4 transformationMatrix = transformationMatrices[drawId]; #else #error #endif #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(NORMAL_DIRECTION) - mediump const mat3 normalMatrix = draws[drawOffset].normalMatrix; + mediump const mat3 normalMatrix = draws[drawId].normalMatrix; #endif - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp float colorMapOffset = materials[materialId].material_colorMapOffset; lowp float colorMapScale = materials[materialId].material_colorMapScale; highp float lineLength = materials[materialId].material_lineLength; diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 5c9488408..99cfa37f1 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -97,6 +97,17 @@ MeshVisualizerGLBase::MeshVisualizerGLBase(FlagsBase flags if(flags >= FlagBase::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= FlagBase::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(_flags & FlagBase::Wireframe && !(_flags & FlagBase::NoGeometryShader)) { @@ -169,6 +180,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + vert.addSource(_flags >= FlagBase::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") @@ -189,6 +201,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + frag.addSource(_flags >= FlagBase::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif @@ -358,6 +371,7 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + geom->addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif geom->addSource(rs.get("MeshVisualizer.geom")); @@ -673,6 +687,7 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + geom->addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif geom->addSource(rs.get("MeshVisualizer.geom")); @@ -990,6 +1005,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { #endif #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -1022,6 +1038,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { #endif #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -1044,6 +1061,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) { MeshVisualizerGL2D::Flag::PrimitiveId, #endif #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL2D::Flag::MultiDraw, /* Superset of UniformBuffers */ MeshVisualizerGL2D::Flag::UniformBuffers #endif #endif @@ -1070,6 +1088,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flags value) { MeshVisualizerGL3D::Flag::PrimitiveId, #endif #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL3D::Flag::MultiDraw, /* Superset of UniformBuffers */ MeshVisualizerGL3D::Flag::UniformBuffers #endif #endif diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index fa750c895..6e0df5a3c 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -55,7 +55,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr PrimitiveId = 1 << 4, PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId, /* bit 6, 7, 8, 9 used by 3D-specific TBN visualization */ - UniformBuffers = 1 << 10 + UniformBuffers = 1 << 10, + MultiDraw = UniformBuffers|(1 << 11) #endif }; typedef Containers::EnumSet FlagsBase; @@ -236,7 +237,31 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 10 + UniformBuffers = 1 << 10, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and combines the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 11) #endif }; @@ -470,6 +495,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than * @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. @@ -997,7 +1027,31 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 10 + UniformBuffers = 1 << 10, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and combines the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 11) #endif }; @@ -1382,6 +1436,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * used for current draw. Expects that @ref Flag::UniformBuffers is set * and @p offset is less than @ref drawCount(). Initial value is * @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 07c11a2fe..390e01e2f 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -148,6 +148,7 @@ uniform lowp float lightRanges[LIGHT_COUNT] /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -156,6 +157,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif /* Keep in sync with Phong.vert. Can't "outsource" to a common file because the #extension directive needs to be always before any code. */ @@ -277,6 +280,10 @@ in lowp vec4 interpolatedVertexColor; flat in highp uint interpolatedInstanceObjectId; #endif +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* Outputs */ #ifdef NEW_GLSL @@ -296,9 +303,9 @@ out highp uint fragmentObjectId; void main() { #ifdef UNIFORM_BUFFERS #ifdef OBJECT_ID - highp const uint objectId = draws[drawOffset].draw_objectId; + highp const uint objectId = draws[drawId].draw_objectId; #endif - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp const vec4 ambientColor = materials[materialId].ambientColor; #if LIGHT_COUNT lowp const vec4 diffuseColor = materials[materialId].diffuseColor; @@ -312,7 +319,7 @@ void main() { lowp const float alphaMask = materials[materialId].material_alphaMask; #endif #if LIGHT_COUNT - mediump const uint lightOffset = draws[drawOffset].draw_lightOffset; + mediump const uint lightOffset = draws[drawId].draw_lightOffset; #endif #endif @@ -370,7 +377,7 @@ void main() { #ifndef UNIFORM_BUFFERS for(int i = 0; i < LIGHT_COUNT; ++i) #else - for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawOffset].draw_lightCount); i < actualLightCount; ++i) + for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i) #endif { lowp const vec3 lightColor = diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index d4a4c5514..97f5920ba 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -27,6 +27,14 @@ #extension GL_EXT_gpu_shader4: require #endif +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -273,17 +281,33 @@ out highp vec4 lightDirections[LIGHT_COUNT]; out highp vec3 cameraDirection; #endif +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS - highp const mat4 transformationMatrix = transformationMatrices[drawOffset]; + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + + highp const mat4 transformationMatrix = transformationMatrices[drawId]; #if LIGHT_COUNT - mediump const mat3 normalMatrix = draws[drawOffset].normalMatrix; + mediump const mat3 normalMatrix = draws[drawId].normalMatrix; #endif #ifdef TEXTURE_TRANSFORMATION - mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); #endif #if LIGHT_COUNT - mediump const uint lightOffset = draws[drawOffset].draw_lightOffset; + mediump const uint lightOffset = draws[drawId].draw_lightOffset; #endif #endif @@ -328,7 +352,7 @@ void main() { #ifndef UNIFORM_BUFFERS for(int i = 0; i < LIGHT_COUNT; ++i) #else - for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawOffset].draw_lightCount); i < actualLightCount; ++i) + for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i) #endif { highp const vec4 lightPosition = diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 00e1083da..377b5443b 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -105,6 +105,17 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -199,6 +210,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount "#define LIGHT_COUNT {}\n", drawCount, lightCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif #ifndef MAGNUM_TARGET_GLES @@ -229,6 +241,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount drawCount, materialCount, lightCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } else #endif { @@ -809,6 +822,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { _c(InstancedTextureOffset) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -834,6 +848,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { #endif PhongGL::Flag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 + PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */ PhongGL::Flag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index cb67a8a02..7e949613c 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -577,7 +577,31 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 12 + UniformBuffers = 1 << 12, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 13) #endif }; @@ -1162,6 +1186,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * and @ref bindTextureTransformationBuffer() should be used for * current draw. Expects that @ref Flag::UniformBuffers is set and * @p offset is less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 055cde641..518286fcb 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -129,24 +129,27 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { Rendering tests done: [B] base - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BO - ES2 x - ES3 BO + Mesa Intel BOM + ES2 xx + ES3 BOx Mesa AMD B Mesa llvmpipe B - SwiftShader ES2 Bx + SwiftShader ES2 Bxx ES3 B - ARM Mali (Huawei P10) ES2 Bx - ES3 BO - WebGL (on Mesa Intel) 1.0 Bx - 2.0 BO + ANGLE ES2 xx + ES3 BOM + ARM Mali (Huawei P10) ES2 Bxx + ES3 BOx + WebGL (on Mesa Intel) 1.0 Bxx + 2.0 BOM NVidia Intel Windows - AMD macOS - Intel macOS BO - iPhone 6 w/ iOS 12.4 ES3 B + AMD macOS x + Intel macOS BOx + iPhone 6 w/ iOS 12.4 ES3 B x */ using namespace Math::Literals; @@ -171,6 +174,7 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case and 3+1 in 2D, per-material 4 */ {"multiple materials, draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 16, 48}, + {"multidraw with all the things", DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation, 16, 48} }; constexpr struct { @@ -213,16 +217,21 @@ constexpr struct { const char* name; const char* expected2D; const char* expected3D; + DistanceFieldVectorGL2D::Flags flags; UnsignedInt materialCount, drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - 1, 1, 16, + {}, 1, 1, 16, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, {"draw offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - 2, 3, 1, + {}, 2, 3, 1, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, + {"multidraw", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::MultiDraw, 2, 3, 1, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, }; @@ -369,6 +378,19 @@ template void DistanceFieldVectorGLTest::constructUnifor CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + DistanceFieldVectorGL shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.materialCount(), data.materialCount); @@ -1036,6 +1058,19 @@ void DistanceFieldVectorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1131,7 +1166,7 @@ void DistanceFieldVectorGLTest::renderMulti2D() { .setMaterialId(data.drawCount == 1 ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, data.materialCount, data.drawCount}; + DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation|data.flags, data.materialCount, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1178,18 +1213,23 @@ void DistanceFieldVectorGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } /* @@ -1214,6 +1254,19 @@ void DistanceFieldVectorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1314,7 +1367,7 @@ void DistanceFieldVectorGLTest::renderMulti3D() { .setMaterialId(data.drawCount == 1 ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation, data.materialCount, data.drawCount}; + DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation|data.flags, data.materialCount, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1361,18 +1414,23 @@ void DistanceFieldVectorGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= DistanceFieldVectorGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } /* diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp index b337b24c5..c9e206ae4 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp @@ -41,17 +41,25 @@ struct DistanceFieldVectorGL_Test: TestSuite::Tester { void debugFlag(); void debugFlags(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets(); + #endif }; DistanceFieldVectorGL_Test::DistanceFieldVectorGL_Test() { - addTests({&DistanceFieldVectorGL_Test::constructNoCreate<2>, - &DistanceFieldVectorGL_Test::constructNoCreate<3>, - - &DistanceFieldVectorGL_Test::constructCopy<2>, - &DistanceFieldVectorGL_Test::constructCopy<3>, - - &DistanceFieldVectorGL_Test::debugFlag, - &DistanceFieldVectorGL_Test::debugFlags}); + addTests({ + &DistanceFieldVectorGL_Test::constructNoCreate<2>, + &DistanceFieldVectorGL_Test::constructNoCreate<3>, + + &DistanceFieldVectorGL_Test::constructCopy<2>, + &DistanceFieldVectorGL_Test::constructCopy<3>, + + &DistanceFieldVectorGL_Test::debugFlag, + &DistanceFieldVectorGL_Test::debugFlags, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGL_Test::debugFlagsSupersets + #endif + }); } template void DistanceFieldVectorGL_Test::constructNoCreate() { @@ -87,6 +95,15 @@ void DistanceFieldVectorGL_Test::debugFlags() { CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::TextureTransformation|Shaders::DistanceFieldVectorGL::Flag(0xf0) Shaders::DistanceFieldVectorGL::Flags{}\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void DistanceFieldVectorGL_Test::debugFlagsSupersets() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (DistanceFieldVectorGL3D::Flag::MultiDraw|DistanceFieldVectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::DistanceFieldVectorGL_Test) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 6d5d4f093..974365108 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -161,24 +161,27 @@ struct FlatGLTest: GL::OpenGLTester { [A] alpha mask [D] object ID [I] instancing - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BADIO - ES2 x - ES3 BADIO + Mesa Intel BADIOM + ES2 xx + ES3 BADIOx Mesa AMD BADI Mesa llvmpipe BADI - SwiftShader ES2 BADIx + SwiftShader ES2 BADIxx ES3 BADI - ARM Mali (Huawei P10) ES2 BAD x - ES3 BADIO - WebGL (on Mesa Intel) 1.0 BAD x - 2.0 BADIO + ANGLE ES2 xx + ES3 BADIOM + ARM Mali (Huawei P10) ES2 BAD xx + ES3 BADIOx + WebGL (on Mesa Intel) 1.0 BAD xx + 2.0 BADIOM NVidia BAD Intel Windows BAD AMD macOS BAD - Intel macOS BADIO - iPhone 6 w/ iOS 12.4 ES3 BAD + Intel macOS BADIOx + iPhone 6 w/ iOS 12.4 ES3 BAD x */ using namespace Math::Literals; @@ -216,7 +219,8 @@ constexpr struct { {"multiple draws", FlatGL2D::Flag::UniformBuffers, 42}, {"texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, {"alpha mask", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::AlphaMask, 1}, - {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 1} + {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 1}, + {"multidraw with all the things", FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId, 42} }; #endif @@ -290,11 +294,19 @@ constexpr struct { /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", - {}, 3, 1, 0.0f, 0.0f}, + {}, + 3, 1, 0.0f, 0.0f}, {"draw offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, 3, 1, /* Minor differences on ARM Mali */ + 2.34f, 0.01f}, + {"multidraw, colored", "multidraw2D.tga", "multidraw3D.tga", + FlatGL2D::Flag::MultiDraw, 3, 1, 0.0f, 0.0f}, + {"multidraw, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, + 3, 1, + /* Minor differences on ARM Mali */ 2.34f, 0.01f} }; #endif @@ -532,6 +544,19 @@ template void FlatGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + if(data.flags >= FlatGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + FlatGL shader{data.flags, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); @@ -2266,6 +2291,19 @@ void FlatGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= FlatGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + GL::Texture2D texture; if(data.flags & FlatGL2D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || @@ -2402,18 +2440,23 @@ void FlatGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); if(data.flags & FlatGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= FlatGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } MAGNUM_VERIFY_NO_GL_ERROR(); @@ -2469,6 +2512,19 @@ void FlatGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= FlatGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + GL::Texture2D texture; if(data.flags & FlatGL3D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || @@ -2611,18 +2667,23 @@ void FlatGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); if(data.flags & FlatGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= FlatGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } MAGNUM_VERIFY_NO_GL_ERROR(); diff --git a/src/Magnum/Shaders/Test/FlatGL_Test.cpp b/src/Magnum/Shaders/Test/FlatGL_Test.cpp index 640e18d78..f4e9c183c 100644 --- a/src/Magnum/Shaders/Test/FlatGL_Test.cpp +++ b/src/Magnum/Shaders/Test/FlatGL_Test.cpp @@ -102,9 +102,20 @@ void FlatGL_Test::debugFlagsSupersets() { /* InstancedTextureOffset is a superset of TextureTransformation so only one should be printed */ - std::ostringstream out; - Debug{&out} << (FlatGL3D::Flag::InstancedTextureOffset|FlatGL3D::Flag::TextureTransformation); - CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::InstancedTextureOffset\n"); + { + std::ostringstream out; + Debug{&out} << (FlatGL3D::Flag::InstancedTextureOffset|FlatGL3D::Flag::TextureTransformation); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::InstancedTextureOffset\n"); + } + + #ifndef MAGNUM_TARGET_GLES2 + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + { + std::ostringstream out; + Debug{&out} << (FlatGL3D::Flag::MultiDraw|FlatGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::MultiDraw\n"); + } + #endif } }}}} diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 0b6bdbd92..9c13ebba6 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -180,23 +180,26 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { [D] primitive/vertex/object ID [T] TBN visualization [O] draw offset + [M] multidraw - Mesa Intel WDTO - ES2 x - ES3 + Mesa Intel WDTOM + ES2 xx + ES3 x Mesa AMD WDT Mesa llvmpipe WDT - SwiftShader ES2 WDxx + SwiftShader ES2 WDxxx ES3 WDx - ARM Mali (Huawei P10) ES2 W xx - ES3 W O (WDT big diffs, needs investigation) - WebGL (on Mesa Intel) 1.0 W xx - 2.0 W x + ANGLE ES2 xx + ES3 WDxOM + ARM Mali (Huawei P10) ES2 W xxx + ES3 W Ox (WDT big diffs, needs investigation) + WebGL (on Mesa Intel) 1.0 W xxx + 2.0 W x M NVidia Intel Windows AMD macOS - Intel macOS WDTO - iPhone 6 w/ iOS 12.4 ES3 W x + Intel macOS WDTOx + iPhone 6 w/ iOS 12.4 ES3 W x x */ using namespace Math::Literals; @@ -232,6 +235,10 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-2D-draw is 4, per-material 4, two need to be left for drawOffset + viewportSize */ {"multiple materials, draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 8, 55}, + {"multidraw with wireframe w/o GS and vertex ID", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader|MeshVisualizerGL2D::Flag::VertexId, 8, 55}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw with wireframe and primitive ID", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::PrimitiveId, 8, 55}, + #endif /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -293,6 +300,10 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-3D-draw is 4+4, per-material 4, plus 4 for projection */ {"multiple materials, draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 6, 28}, + {"multidraw with wireframe w/o GS and vertex ID", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader|MeshVisualizerGL3D::Flag::VertexId, 6, 28}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw with wireframe, primitive ID and TBN", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::PrimitiveId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, 6, 28}, + #endif /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -605,6 +616,23 @@ constexpr struct { 2, 3, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, wireframe", "multidraw-wireframe2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, + #endif + {"multidraw, wireframe w/o GS", "multidraw-wireframe-nogeo2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.02f}, + {"multidraw, vertex ID", "multidraw-vertexid2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::VertexId, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, }; constexpr struct { @@ -651,6 +679,24 @@ constexpr struct { 2, 3, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, wireframe", "multidraw-wireframe3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe, + 2, 3, 1, 0.0f, 0.0f}, + {"multidraw, wireframe + TBN", "multidraw-wireframe-tbn3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, + 2, 3, 1, 0.0f, 0.0f}, + #endif + {"multidraw, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 6.0f, 0.04f}, + {"multidraw, vertex ID", "multidraw-vertexid3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::VertexId, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, }; #endif @@ -998,6 +1044,19 @@ void MeshVisualizerGLTest::constructUniformBuffers2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + MeshVisualizerGL2D shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -1123,6 +1182,19 @@ void MeshVisualizerGLTest::constructUniformBuffers3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + MeshVisualizerGL3D shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -3158,6 +3230,19 @@ void MeshVisualizerGLTest::renderMulti2D() { } #endif + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(8)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); @@ -3273,17 +3358,22 @@ void MeshVisualizerGLTest::renderMulti2D() { sizeof(MeshVisualizerDrawUniform2D)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindMaterialBuffer(materialUniform) .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } }; MAGNUM_VERIFY_NO_GL_ERROR(); @@ -3333,6 +3423,19 @@ void MeshVisualizerGLTest::renderMulti3D() { } #endif + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + Trade::MeshData sphereData = MeshTools::interleave(Primitives::icosphereSolid(0), { /* The icosphere doesn't have tangents and we don't use them, but concatenate() will ignore the tangents of others if the first mesh @@ -3462,17 +3565,22 @@ void MeshVisualizerGLTest::renderMulti3D() { sizeof(MeshVisualizerDrawUniform3D)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindMaterialBuffer(materialUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } }; MAGNUM_VERIFY_NO_GL_ERROR(); diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp index acee35225..10e7ffc53 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp @@ -48,21 +48,31 @@ struct MeshVisualizerGL_Test: TestSuite::Tester { void debugFlag3D(); void debugFlags2D(); void debugFlags3D(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets2D(); + void debugFlagsSupersets3D(); + #endif }; MeshVisualizerGL_Test::MeshVisualizerGL_Test() { - addTests({&MeshVisualizerGL_Test::constructNoCreate2D, - &MeshVisualizerGL_Test::constructNoCreate3D, - - &MeshVisualizerGL_Test::constructCopy2D, - &MeshVisualizerGL_Test::constructCopy3D, - - &MeshVisualizerGL_Test::vertexIndexSameAsObjectId, - - &MeshVisualizerGL_Test::debugFlag2D, - &MeshVisualizerGL_Test::debugFlag3D, - &MeshVisualizerGL_Test::debugFlags2D, - &MeshVisualizerGL_Test::debugFlags3D}); + addTests({ + &MeshVisualizerGL_Test::constructNoCreate2D, + &MeshVisualizerGL_Test::constructNoCreate3D, + + &MeshVisualizerGL_Test::constructCopy2D, + &MeshVisualizerGL_Test::constructCopy3D, + + &MeshVisualizerGL_Test::vertexIndexSameAsObjectId, + + &MeshVisualizerGL_Test::debugFlag2D, + &MeshVisualizerGL_Test::debugFlag3D, + &MeshVisualizerGL_Test::debugFlags2D, + &MeshVisualizerGL_Test::debugFlags3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGL_Test::debugFlagsSupersets2D, + &MeshVisualizerGL_Test::debugFlagsSupersets3D, + #endif + }); } void MeshVisualizerGL_Test::constructNoCreate2D() { @@ -140,6 +150,22 @@ void MeshVisualizerGL_Test::debugFlags3D() { #endif } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGL_Test::debugFlagsSupersets2D() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::MultiDraw\n"); +} + +void MeshVisualizerGL_Test::debugFlagsSupersets3D() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::MeshVisualizerGL_Test) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 4a952bce2..ce99031f0 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -168,24 +168,27 @@ struct PhongGLTest: GL::OpenGLTester { [D] object ID [L] point lights [I] instancing - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BADLIO - ES2 x - ES3 BADLIO + Mesa Intel BADLIOM + ES2 xx + ES3 BADLIOx Mesa AMD BAD I Mesa llvmpipe BAD I - SwiftShader ES2 BADLIx + SwiftShader ES2 BADLIxx ES3 BADLI - ARM Mali (Huawei P10) ES2 BAD x - ES3 BADLIO - WebGL (on Mesa Intel) 1.0 BAD x - 2.0 BADLIO + ANGLE ES2 xx + ES3 BADLIOM + ARM Mali (Huawei P10) ES2 BAD xx + ES3 BADLIOx + WebGL (on Mesa Intel) 1.0 BAD xx + 2.0 BADLIOM NVidia BAD Intel Windows BAD AMD macOS BAD - Intel macOS BADLIO - iPhone 6 w/ iOS 12.4 ES3 BAD + Intel macOS BADLIOx + iPhone 6 w/ iOS 12.4 ES3 BAD x */ constexpr struct { @@ -244,7 +247,8 @@ constexpr struct { {"normal texture", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture, 1, 1, 1}, {"normal texture + separate bitangents", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1}, {"alpha mask", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AlphaMask, 1, 1, 1}, - {"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1} + {"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1}, + {"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 8, 16, 24} }; #endif @@ -617,6 +621,16 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, + {"multidraw, colored", "multidraw.tga", + PhongGL::Flag::MultiDraw, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, + {"multidraw, textured", "multidraw-textured.tga", + PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 4.67f, 0.02f}, }; #endif @@ -875,6 +889,19 @@ void PhongGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + PhongGL shader{data.flags, data.lightCount, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.lightCount(), data.lightCount); @@ -2971,6 +2998,19 @@ void PhongGLTest::renderMulti() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + GL::Texture2D diffuse; if(data.flags & PhongGL::Flag::DiffuseTexture) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || @@ -3176,7 +3216,7 @@ void PhongGLTest::renderMulti() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) @@ -3184,12 +3224,17 @@ void PhongGLTest::renderMulti() { .bindLightBuffer(lightUniform); if(data.flags & PhongGL::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= PhongGL::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } /* diff --git a/src/Magnum/Shaders/Test/PhongGL_Test.cpp b/src/Magnum/Shaders/Test/PhongGL_Test.cpp index b76638f6c..b00618d21 100644 --- a/src/Magnum/Shaders/Test/PhongGL_Test.cpp +++ b/src/Magnum/Shaders/Test/PhongGL_Test.cpp @@ -96,9 +96,20 @@ void PhongGL_Test::debugFlagsSupersets() { /* InstancedTextureOffset is a superset of TextureTransformation so only one should be printed */ - std::ostringstream out; - Debug{&out} << (PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::TextureTransformation); - CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::InstancedTextureOffset\n"); + { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::TextureTransformation); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::InstancedTextureOffset\n"); + } + + #ifndef MAGNUM_TARGET_GLES2 + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::MultiDraw|PhongGL::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::MultiDraw\n"); + } + #endif } }}}} diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 04ba121a9..1f5260c95 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -128,24 +128,27 @@ struct VectorGLTest: GL::OpenGLTester { Rendering tests done: [B] base - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BO - ES2 x - ES3 BO + Mesa Intel BOM + ES2 xx + ES3 BOx Mesa AMD B Mesa llvmpipe B - SwiftShader ES2 Bx + SwiftShader ES2 Bxx ES3 B - ARM Mali (Huawei P10) ES2 Bx - ES3 BO - WebGL (on Mesa Intel) 1.0 Bx - 2.0 BO + ANGLE ES2 xx + ES3 BOM + ARM Mali (Huawei P10) ES2 Bxx + ES3 BOx + WebGL (on Mesa Intel) 1.0 Bxx + 2.0 BOM NVidia Intel Windows - AMD macOS - Intel macOS BO - iPhone 6 w/ iOS 12.4 ES3 B + AMD macOS x + Intel macOS BOx + iPhone 6 w/ iOS 12.4 ES3 B x */ using namespace Math::Literals; @@ -170,6 +173,7 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-draw is 4+3 in 3D case and 3+3 in 2D */ {"multiple draws", VectorGL2D::Flag::UniformBuffers, 36}, + {"multidraw with all the things", VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 36} }; #endif @@ -195,16 +199,21 @@ constexpr struct { const char* name; const char* expected2D; const char* expected3D; + VectorGL2D::Flags flags; UnsignedInt drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - 1, 16, + {}, 1, 16, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - 3, 1, + {}, 3, 1, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, + {"multidraw", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::MultiDraw, 3, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, }; @@ -347,6 +356,19 @@ template void VectorGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + VectorGL shader{data.flags, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); @@ -958,6 +980,19 @@ void VectorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1045,7 +1080,7 @@ void VectorGLTest::renderMulti2D() { .setBackgroundColor(0xccffcc_rgbf); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, data.drawCount}; + VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation|data.flags, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1083,17 +1118,22 @@ void VectorGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= VectorGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } /* @@ -1118,6 +1158,19 @@ void VectorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VectorGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1210,7 +1263,7 @@ void VectorGLTest::renderMulti3D() { .setBackgroundColor(0xccffcc_rgbf); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation, data.drawCount}; + VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation|data.flags, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1248,17 +1301,22 @@ void VectorGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= VectorGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } /* diff --git a/src/Magnum/Shaders/Test/VectorGL_Test.cpp b/src/Magnum/Shaders/Test/VectorGL_Test.cpp index ff8e7f2e6..f050f270d 100644 --- a/src/Magnum/Shaders/Test/VectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VectorGL_Test.cpp @@ -41,17 +41,25 @@ struct VectorGL_Test: TestSuite::Tester { void debugFlag(); void debugFlags(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets(); + #endif }; VectorGL_Test::VectorGL_Test() { - addTests({&VectorGL_Test::constructNoCreate<2>, - &VectorGL_Test::constructNoCreate<3>, - - &VectorGL_Test::constructCopy<2>, - &VectorGL_Test::constructCopy<3>, - - &VectorGL_Test::debugFlag, - &VectorGL_Test::debugFlags}); + addTests({ + &VectorGL_Test::constructNoCreate<2>, + &VectorGL_Test::constructNoCreate<3>, + + &VectorGL_Test::constructCopy<2>, + &VectorGL_Test::constructCopy<3>, + + &VectorGL_Test::debugFlag, + &VectorGL_Test::debugFlags, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGL_Test::debugFlagsSupersets + #endif + }); } template void VectorGL_Test::constructNoCreate() { @@ -87,6 +95,15 @@ void VectorGL_Test::debugFlags() { CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::TextureTransformation|Shaders::VectorGL::Flag(0xf0) Shaders::VectorGL::Flags{}\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void VectorGL_Test::debugFlagsSupersets() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (VectorGL3D::Flag::MultiDraw|VectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VectorGL_Test) diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 3179ed70f..bff07e338 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -115,24 +115,27 @@ struct VertexColorGLTest: GL::OpenGLTester { Rendering tests done: [B] base - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BO - ES2 x - ES3 BO + Mesa Intel BOM + ES2 xx + ES3 BOx Mesa AMD B Mesa llvmpipe B - SwiftShader ES2 Bx + SwiftShader ES2 Bxx ES3 B - ARM Mali (Huawei P10) ES2 Bx - ES3 BO - WebGL (on Mesa Intel) 1.0 Bx - 2.0 BO + ANGLE ES2 xx + ES3 BOM + ARM Mali (Huawei P10) ES2 Bxx + ES3 BOx + WebGL (on Mesa Intel) 1.0 Bxx + 2.0 BOM NVidia Intel Windows - AMD macOS - Intel macOS BO - iPhone 6 w/ iOS 12.4 ES3 B + AMD macOS x + Intel macOS BOx + iPhone 6 w/ iOS 12.4 ES3 B x */ using namespace Math::Literals; @@ -147,7 +150,8 @@ constexpr struct { {"", VertexColorGL2D::Flag::UniformBuffers, 1}, /* SwiftShader has 256 uniform vectors at most, per-draw is 4 in 3D case and 3 in 2D; one needs to be reserved for drawOffset */ - {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 63} + {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 63}, + {"multidraw with all the things", VertexColorGL2D::Flag::MultiDraw, 63} }; #endif @@ -156,18 +160,23 @@ constexpr struct { const char* name; const char* expected2D; const char* expected3D; + VertexColorGL2D::Flags flags; UnsignedInt drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - 1, 16, + {}, 1, 16, /* Minor differences on ARM Mali */ 0.34f, 0.01f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - 3, 1, + {}, 3, 1, /* Minor differences on ARM Mali */ 0.34f, 0.01f}, + {"multidraw", "multidraw2D.tga", "multidraw3D.tga", + VertexColorGL2D::Flag::MultiDraw, 3, 1, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f} }; #endif @@ -305,6 +314,19 @@ template void VertexColorGLTest::constructUniformBuffers CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + VertexColorGL shader{data.flags, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); @@ -758,6 +780,19 @@ void VertexColorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); @@ -806,7 +841,7 @@ void VertexColorGLTest::renderMulti2D() { ); GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; - VertexColorGL2D shader{VertexColorGL2D::Flag::UniformBuffers, data.drawCount}; + VertexColorGL2D shader{VertexColorGL2D::Flag::UniformBuffers|data.flags, data.drawCount}; /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { @@ -825,15 +860,20 @@ void VertexColorGLTest::renderMulti2D() { sizeof(TransformationProjectionUniform2D)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } MAGNUM_VERIFY_NO_GL_ERROR(); @@ -863,6 +903,19 @@ void VertexColorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VertexColorGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32); /* Plane is a strip, make it indexed first */ Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid()); @@ -914,7 +967,7 @@ void VertexColorGLTest::renderMulti3D() { ); GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; - VertexColorGL3D shader{VertexColorGL3D::Flag::UniformBuffers, data.drawCount}; + VertexColorGL3D shader{VertexColorGL3D::Flag::UniformBuffers|data.flags, data.drawCount}; /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { @@ -933,15 +986,20 @@ void VertexColorGLTest::renderMulti3D() { sizeof(TransformationProjectionUniform3D)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= VertexColorGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } MAGNUM_VERIFY_NO_GL_ERROR(); diff --git a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp index 1f86165ac..949670a1c 100644 --- a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp @@ -41,6 +41,9 @@ struct VertexColorGL_Test: TestSuite::Tester { void debugFlag(); void debugFlags(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets(); + #endif }; VertexColorGL_Test::VertexColorGL_Test() { @@ -52,7 +55,11 @@ VertexColorGL_Test::VertexColorGL_Test() { &VertexColorGL_Test::constructCopy<3>, &VertexColorGL_Test::debugFlag, - &VertexColorGL_Test::debugFlags}); + &VertexColorGL_Test::debugFlags, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGL_Test::debugFlagsSupersets + #endif + }); } template void VertexColorGL_Test::constructNoCreate() { @@ -97,6 +104,15 @@ void VertexColorGL_Test::debugFlags() { #endif } +#ifndef MAGNUM_TARGET_GLES2 +void VertexColorGL_Test::debugFlagsSupersets() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (VertexColorGL3D::Flag::MultiDraw|VertexColorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VertexColorGL_Test) diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 94f0bb419..8f1993944 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -53,6 +53,7 @@ uniform lowp vec4 color /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -61,6 +62,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif struct DrawUniform { lowp vec4 color; @@ -88,6 +91,10 @@ uniform lowp sampler2D vectorTexture; in mediump vec2 interpolatedTextureCoordinates; +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* Outputs */ #ifdef NEW_GLSL @@ -99,8 +106,8 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - lowp const vec4 color = draws[drawOffset].color; - lowp const vec4 backgroundColor = draws[drawOffset].backgroundColor; + lowp const vec4 color = draws[drawId].color; + lowp const vec4 backgroundColor = draws[drawId].backgroundColor; #endif lowp float intensity = texture(vectorTexture, interpolatedTextureCoordinates).r; diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index e0fbab898..3f38ac103 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -23,6 +23,14 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -132,8 +140,24 @@ in mediump vec2 textureCoordinates; out mediump vec2 interpolatedTextureCoordinates; +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + highp const #ifdef TWO_DIMENSIONS mat3 @@ -142,9 +166,9 @@ void main() { #else #error #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #ifdef TEXTURE_TRANSFORMATION - mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); #endif #endif diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index e0c2531af..9efddeb6b 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -81,6 +81,17 @@ template VectorGL::VectorGL(const Flags flag if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -108,6 +119,7 @@ template VectorGL::VectorGL(const Flags flag "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -118,6 +130,7 @@ template VectorGL::VectorGL(const Flags flag "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(rs.get("generic.glsl")) @@ -308,6 +321,7 @@ Debug& operator<<(Debug& debug, const VectorGLFlag value) { _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -320,6 +334,7 @@ Debug& operator<<(Debug& debug, const VectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::VectorGL::Flags{}", { VectorGLFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + VectorGLFlag::MultiDraw, /* Superset of UniformBuffers */ VectorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index c899e5130..898ac1ba3 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -41,7 +41,8 @@ namespace Implementation { enum class VectorGLFlag: UnsignedByte { TextureTransformation = 1 << 0, #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + MultiDraw = UniformBuffers|(1 << 2) #endif }; typedef Containers::EnumSet VectorGLFlags; @@ -135,7 +136,31 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 2) #endif }; @@ -327,6 +352,11 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than * @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/VertexColor.vert b/src/Magnum/Shaders/VertexColor.vert index c7597a413..53f389cae 100644 --- a/src/Magnum/Shaders/VertexColor.vert +++ b/src/Magnum/Shaders/VertexColor.vert @@ -23,6 +23,14 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -107,6 +115,18 @@ out lowp vec4 interpolatedColor; void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + highp uint drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + highp const #ifdef TWO_DIMENSIONS mat3 @@ -115,7 +135,7 @@ void main() { #else #error #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #endif #ifdef TWO_DIMENSIONS diff --git a/src/Magnum/Shaders/VertexColorGL.cpp b/src/Magnum/Shaders/VertexColorGL.cpp index 4c1b4eb3f..7da23cbc4 100644 --- a/src/Magnum/Shaders/VertexColorGL.cpp +++ b/src/Magnum/Shaders/VertexColorGL.cpp @@ -76,6 +76,17 @@ template VertexColorGL::VertexColorGL(const if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -102,6 +113,7 @@ template VertexColorGL::VertexColorGL(const "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -214,6 +226,7 @@ Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { #define _c(v) case VertexColorGLFlag::v: return debug << "::" #v; #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -225,6 +238,7 @@ Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { Debug& operator<<(Debug& debug, const VertexColorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::VertexColorGL::Flags{}", { #ifndef MAGNUM_TARGET_GLES2 + VertexColorGLFlag::MultiDraw, /* Superset of UniformBuffers */ VertexColorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index d84acec62..7df6556e1 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -40,7 +40,8 @@ namespace Magnum { namespace Shaders { namespace Implementation { enum class VertexColorGLFlag: UnsignedByte { #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 0 + UniformBuffers = 1 << 0, + MultiDraw = UniformBuffers|(1 << 1) #endif }; typedef Containers::EnumSet VertexColorGLFlags; @@ -136,7 +137,31 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 0 + UniformBuffers = 1 << 0, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 1) #endif }; @@ -279,6 +304,11 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * @ref bindTransformationProjectionBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0.