From b38d3eea89d20e3c6ff67864df041f7c76a6c7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 13 Mar 2023 23:43:14 +0100 Subject: [PATCH] Shaders: add SSBO support to all shaders. For when there's so much to render that it wouldn't fit into an UBO and splitting draw calls and binding buffers under an offset is unwanted overhead. --- doc/changelog.dox | 7 +- doc/shaders.dox | 5 + package/archlinux/PKGBUILD | 2 +- package/archlinux/PKGBUILD-coverage | 2 +- src/Magnum/Shaders/DistanceFieldVector.frag | 37 +- src/Magnum/Shaders/DistanceFieldVectorGL.cpp | 158 +++- src/Magnum/Shaders/DistanceFieldVectorGL.h | 54 +- src/Magnum/Shaders/Flat.frag | 37 +- src/Magnum/Shaders/Flat.vert | 49 +- src/Magnum/Shaders/FlatGL.cpp | 186 +++- src/Magnum/Shaders/FlatGL.h | 71 +- src/Magnum/Shaders/Line.frag | 37 +- src/Magnum/Shaders/Line.vert | 43 +- src/Magnum/Shaders/LineGL.cpp | 151 ++- src/Magnum/Shaders/LineGL.h | 55 +- src/Magnum/Shaders/MeshVisualizer.frag | 37 +- src/Magnum/Shaders/MeshVisualizer.geom | 33 +- src/Magnum/Shaders/MeshVisualizer.vert | 72 +- src/Magnum/Shaders/MeshVisualizerGL.cpp | 325 +++++-- src/Magnum/Shaders/MeshVisualizerGL.h | 139 ++- src/Magnum/Shaders/Phong.frag | 48 +- src/Magnum/Shaders/Phong.vert | 55 +- src/Magnum/Shaders/PhongGL.cpp | 222 ++++- src/Magnum/Shaders/PhongGL.h | 85 +- .../Test/DistanceFieldVectorGLTest.cpp | 179 +++- .../Test/DistanceFieldVectorGL_Test.cpp | 22 +- src/Magnum/Shaders/Test/FlatGLTest.cpp | 757 +++++++++++++-- src/Magnum/Shaders/Test/FlatGL_Test.cpp | 25 +- src/Magnum/Shaders/Test/LineGLTest.cpp | 358 +++++++- src/Magnum/Shaders/Test/LineGL_Test.cpp | 17 +- .../Shaders/Test/MeshVisualizerGLTest.cpp | 867 +++++++++++++++--- .../Shaders/Test/MeshVisualizerGL_Test.cpp | 39 +- src/Magnum/Shaders/Test/PhongGLTest.cpp | 615 +++++++++++-- src/Magnum/Shaders/Test/PhongGL_Test.cpp | 36 +- src/Magnum/Shaders/Test/VectorGLTest.cpp | 179 +++- src/Magnum/Shaders/Test/VectorGL_Test.cpp | 22 +- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 179 +++- .../Shaders/Test/VertexColorGL_Test.cpp | 22 +- src/Magnum/Shaders/Vector.frag | 37 +- src/Magnum/Shaders/Vector.vert | 32 +- src/Magnum/Shaders/VectorGL.cpp | 158 +++- src/Magnum/Shaders/VectorGL.h | 54 +- src/Magnum/Shaders/VertexColor.vert | 26 +- src/Magnum/Shaders/VertexColorGL.cpp | 86 +- src/Magnum/Shaders/VertexColorGL.h | 36 +- 45 files changed, 4686 insertions(+), 970 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 54e0be015..e200b6edd 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -278,9 +278,10 @@ See also: [mosra/magnum#601](https://github.com/mosra/magnum/pull/601) and [mosra/magnum#610](https://github.com/mosra/magnum/pull/610). - All builtin shaders now have opt-in support for uniform buffers on desktop, - OpenGL ES 3.0+ and WebGL 2.0, including multi-draw functionality for - massive driver overhead reduction. The @ref shaders overview page was - updated with an introduction the new features. + OpenGL ES 3.0+ and WebGL 2.0, as well as shader storage buffers on desktop + and ES 3.1+. This includes multi-draw functionality for massive driver + overhead reduction. The @ref shaders overview page was updated with an + introduction to the new features. - All builtin shaders now have opt-in capability of @ref shaders-async "async compilation and linking" (see [mosra/magnum534](https://github.com/mosra/magnum/issues/534) and diff --git a/doc/shaders.dox b/doc/shaders.dox index 615420471..3ce33fd69 100644 --- a/doc/shaders.dox +++ b/doc/shaders.dox @@ -207,6 +207,11 @@ like this, uniform upload and binding is the same as before: fit into the limit. For convenience, all uniform structures are guaranteed to fit evenly into multiples of 768 bytes, which should be large enough for even the strictest @ref GL::Buffer::uniformOffsetAlignment() requirements. +@par + An alternative solution to overcome the size limits is to enable @relativeref{Shaders::PhongGL,Flag::ShaderStorageBuffers} instead + (available on OpenGL 4.3+ and OpenGL ES 3.1+), which have no size limits. + The cost is narrower platform support and potentially slower access + compared to uniform buffers. @subsection shaders-usage-instancing Instancing diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 8b5450314..bea9ac9b9 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -74,7 +74,7 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" ctest -C $config --output-on-failure -j9 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest -C $config --output-on-failure -j9 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest -C $config --output-on-failure -j9 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object" ctest -C $config --output-on-failure -j9 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object GL_ARB_shader_storage_buffer_object" ctest -C $config --output-on-failure -j9 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest -C $config --output-on-failure -j9 -R GLTest # Run all Vulkan tests with llvmpipe as well diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index d65313dee..a4eade3cd 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -74,7 +74,7 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access GL_ARB_robustness GL_ARB_multi_bind" ctest --output-on-failure -j9 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j9 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j9 -R GLTest || true - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object" ctest --output-on-failure -j9 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object GL_ARB_shader_storage_buffer_object" ctest --output-on-failure -j9 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j9 -R GLTest || true # Run all Vulkan tests with llvmpipe as well diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 51a09beeb..740b53ad9 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -23,6 +23,10 @@ DEALINGS IN THE SOFTWARE. */ +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifndef NEW_GLSL #define in varying #define fragmentColor gl_FragColor @@ -68,11 +72,24 @@ uniform lowp float smoothness #endif ; -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW -#if DRAW_COUNT > 1 +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -93,11 +110,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; struct MaterialUniform { @@ -110,11 +127,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -144,7 +161,9 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 63bed096d..fa35010ee 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -68,17 +68,31 @@ namespace { } template typename DistanceFieldVectorGL::CompileState DistanceFieldVectorGL::compile(const Configuration& configuration) { - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::DistanceFieldVectorGL: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::DistanceFieldVectorGL: draw count can't be zero", CompileState{NoCreate}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_NO_ASSERT) + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::DistanceFieldVectorGL: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::DistanceFieldVectorGL: draw count can't be zero", CompileState{NoCreate}); + } #endif #ifndef MAGNUM_TARGET_GLES if(configuration.flags() >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES @@ -115,10 +129,21 @@ template typename DistanceFieldVectorGL::Com .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n"_s : "#define THREE_DIMENSIONS\n"_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - configuration.drawCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + configuration.drawCount())); + } vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -129,12 +154,23 @@ template typename DistanceFieldVectorGL::Com GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define MATERIAL_COUNT {}\n" - "#define DRAW_COUNT {}\n", - configuration.materialCount(), - configuration.drawCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw and material arrays so just a plain + string can be passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + frag.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + frag.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define MATERIAL_COUNT {}\n" + "#define DRAW_COUNT {}\n", + configuration.materialCount(), + configuration.drawCount())); + } frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -204,7 +240,11 @@ template DistanceFieldVectorGL::DistanceFiel { #ifndef MAGNUM_TARGET_GLES2 if(_flags >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -226,7 +266,12 @@ template DistanceFieldVectorGL::DistanceFiel { setUniform(uniformLocation("vectorTexture"_s), TextureUnit); #ifndef MAGNUM_TARGET_GLES2 - if(_flags >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(_flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(_flags >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"_s), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"_s), MaterialBufferBinding); @@ -331,37 +376,62 @@ template DistanceFieldVectorGL& DistanceFiel template DistanceFieldVectorGL& DistanceFieldVectorGL::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= Flag::ShaderStorageBuffers || offset < _drawCount, + "Shaders::DistanceFieldVectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::DistanceFieldVectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + #endif + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || _flags >= Flag::ShaderStorageBuffers + #endif + ) setUniform(_drawOffsetUniform, offset); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } @@ -370,7 +440,11 @@ template DistanceFieldVectorGL& DistanceFiel "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); return *this; } @@ -379,21 +453,33 @@ template DistanceFieldVectorGL& DistanceFiel "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); return *this; } #endif @@ -409,6 +495,14 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVectorGL<3>; namespace Implementation { Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* Special case coming from the Flags printer. As both flags are a superset + of UniformBuffers, printing just one would result in + `Flag::MultiDraw|Flag(0x8)` in the output. */ + if(value == DistanceFieldVectorGLFlag(UnsignedByte(DistanceFieldVectorGLFlag::MultiDraw|DistanceFieldVectorGLFlag::ShaderStorageBuffers))) + return debug << DistanceFieldVectorGLFlag::MultiDraw << Debug::nospace << "|" << Debug::nospace << DistanceFieldVectorGLFlag::ShaderStorageBuffers; + #endif + debug << "Shaders::DistanceFieldVectorGL::Flag" << Debug::nospace; switch(value) { @@ -417,6 +511,9 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) #endif #undef _c @@ -430,7 +527,16 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::DistanceFieldVectorGL::Flags{}", { DistanceFieldVectorGLFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers, meaning printing just one + would result in `Flag::MultiDraw|Flag(0x8)` in the output. So we + pass both and let the Flag printer deal with that. */ + DistanceFieldVectorGLFlag(UnsignedByte(DistanceFieldVectorGLFlag::MultiDraw|DistanceFieldVectorGLFlag::ShaderStorageBuffers)), + #endif DistanceFieldVectorGLFlag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + DistanceFieldVectorGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif DistanceFieldVectorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 2a447e680..8a5cf5745 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -46,6 +46,9 @@ namespace Implementation { TextureTransformation = 1 << 0, #ifndef MAGNUM_TARGET_GLES2 UniformBuffers = 1 << 1, + #ifndef MAGNUM_TARGET_WEBGL + ShaderStorageBuffers = UniformBuffers|(1 << 3), + #endif MultiDraw = UniformBuffers|(1 << 2) #endif }; @@ -178,6 +181,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @ref bindTransformationProjectionBuffer(), * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer(), * and @ref bindMaterialBuffer() instead of direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -187,6 +191,23 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector */ UniformBuffers = 1 << 1, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setMaterialCount() and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 3), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the @@ -344,7 +365,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * Statically defined size of the * @ref DistanceFieldVectorMaterialUniform uniform buffer bound with * @ref bindMaterialBuffer(). Has use only if @ref Flag::UniformBuffers - * is set. + * is set and @ref Flag::ShaderStorageBuffers is not set. * @see @ref Configuration::setMaterialCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -362,7 +383,8 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @ref TextureTransformationUniform uniform buffers bound with * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and * @ref bindTextureTransformationBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -472,7 +494,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -504,7 +526,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector DistanceFieldVectorGL& setDrawOffset(UnsignedInt offset); /** - * @brief Bind a transformation and projection uniform buffer + * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -525,7 +547,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector DistanceFieldVectorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -546,7 +568,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector DistanceFieldVectorGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a texture transformation uniform buffer + * @brief Bind a texture transformation uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -566,7 +588,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector DistanceFieldVectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -675,11 +697,11 @@ template class DistanceFieldVectorGL::Config * @ref DistanceFieldVectorMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(DistanceFieldVectorMaterialUniform) @ce - * has to be within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref DistanceFieldVectorDrawUniform::materialId. Default value is - * @cpp 1 @ce. + * has to be within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), + * if @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref DistanceFieldVectorDrawUniform::materialId. + * Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), @@ -716,10 +738,10 @@ template class DistanceFieldVectorGL::Config * @cpp count*sizeof(TransformationProjectionUniform3D) @ce, * @cpp count*sizeof(DistanceFieldVectorDrawUniform) @ce and * @cpp count*sizeof(TextureTransformationUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 68257f0f9..bdf8e30f2 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifndef NEW_GLSL #define fragmentColor gl_FragColor #define texture texture2D @@ -68,11 +72,24 @@ layout(location = 5) uniform highp uint objectId; /* defaults to zero */ #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW -#if DRAW_COUNT > 1 +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -95,11 +112,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; struct MaterialUniform { @@ -109,11 +126,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -190,7 +207,9 @@ void main() { #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index 8a3b5982a..e8ed80d0d 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -31,6 +31,10 @@ #extension GL_ARB_shader_bit_encoding: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -128,10 +132,27 @@ layout(location = PER_INSTANCE_JOINT_COUNT_LOCATION) uniform uint perInstanceJointCount; /* defaults to zero */ #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else -#if DRAW_COUNT > 1 +/* For SSBOs, the per-draw and joint arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +/* Define JOINT_COUNT only if there are any per-vertex attributes, otherwise + the buffer would be useless */ +#if defined(PER_VERTEX_JOINT_COUNT) || defined(SECONDARY_PER_VERTEX_JOINT_COUNT) +#define JOINT_COUNT +#endif +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -152,19 +173,19 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform TransformationProjection { - highp +) BUFFER_OR_UNIFORM TransformationProjection { + BUFFER_READONLY highp #ifdef TWO_DIMENSIONS /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ @@ -179,11 +200,11 @@ layout(std140 #ifdef JOINT_COUNT layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 6 #endif -) uniform Joint { - highp +) BUFFER_OR_UNIFORM Joint { + BUFFER_READONLY highp #ifdef TWO_DIMENSIONS /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ @@ -206,11 +227,11 @@ struct TextureTransformationUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 3 #endif -) uniform TextureTransformation { - TextureTransformationUniform textureTransformations[DRAW_COUNT]; +) BUFFER_OR_UNIFORM TextureTransformation { + BUFFER_READONLY TextureTransformationUniform textureTransformations[DRAW_COUNT]; }; #endif #endif diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 4264a6983..8bcccb27b 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -89,11 +89,18 @@ template typename FlatGL::CompileState FlatG } #endif - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::FlatGL: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::FlatGL: draw count can't be zero", CompileState{NoCreate}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_NO_ASSERT) + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!configuration.jointCount() == (!configuration.perVertexJointCount() && !configuration.secondaryPerVertexJointCount()), + "Shaders::FlatGL: joint count can't be zero if per-vertex joint count is non-zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::FlatGL: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::FlatGL: draw count can't be zero", CompileState{NoCreate}); + } #endif #ifndef MAGNUM_TARGET_GLES2 @@ -116,6 +123,15 @@ template typename FlatGL::CompileState FlatG if(configuration.flags() >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES @@ -210,9 +226,15 @@ template typename FlatGL::CompileState FlatG #endif { vert.addSource(Utility::format( - "#define JOINT_COUNT {}\n" - "#define PER_VERTEX_JOINT_COUNT {}u\n" - "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n", + /* SSBOs have an unbounded joints array */ + #ifndef MAGNUM_TARGET_WEBGL + configuration.flags() >= Flag::ShaderStorageBuffers ? + "#define PER_VERTEX_JOINT_COUNT {1}u\n" + "#define SECONDARY_PER_VERTEX_JOINT_COUNT {2}u\n" : + #endif + "#define JOINT_COUNT {}\n" + "#define PER_VERTEX_JOINT_COUNT {1}u\n" + "#define SECONDARY_PER_VERTEX_JOINT_COUNT {2}u\n", configuration.jointCount(), configuration.perVertexJointCount(), configuration.secondaryPerVertexJointCount())); @@ -222,7 +244,7 @@ template typename FlatGL::CompileState FlatG #ifndef MAGNUM_TARGET_WEBGL /* The _LOCATION is needed only if explicit uniform location (desktop / ES3.1) is supported, a plain string can be added otherwise. This is - an immediate uniform also in the UBO case. */ + an immediate uniform also in the UBO / SSBO case. */ #ifndef MAGNUM_TARGET_GLES if(context.isExtensionSupported(version)) #else @@ -242,10 +264,21 @@ template typename FlatGL::CompileState FlatG #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - configuration.drawCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + configuration.drawCount())); + } vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -268,12 +301,23 @@ template typename FlatGL::CompileState FlatG ; #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw and material arrays so just a plain + string can be passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + frag.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + frag.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + } frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -372,7 +416,11 @@ template FlatGL::FlatGL(CompileState&& state if(_flags >= Flag::DynamicPerVertexJointCount) _perVertexJointCountUniform = uniformLocation("perVertexJointCount"_s); if(_flags >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -406,7 +454,12 @@ template FlatGL::FlatGL(CompileState&& state if(_flags & Flag::Textured) setUniform(uniformLocation("textureData"_s), TextureUnit); #ifndef MAGNUM_TARGET_GLES2 if(_flags >= Flag::ObjectIdTexture) setUniform(uniformLocation("objectIdTextureData"_s), ObjectIdTextureUnit); - if(_flags >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(_flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(_flags >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"_s), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); if(_flags & Flag::TextureTransformation) @@ -571,37 +624,62 @@ template FlatGL& FlatGL::setPerI template FlatGL& FlatGL::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= Flag::ShaderStorageBuffers || offset < _drawCount, + "Shaders::FlatGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::FlatGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + #endif + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || _flags >= Flag::ShaderStorageBuffers + #endif + ) setUniform(_drawOffsetUniform, offset); return *this; } template FlatGL& FlatGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); return *this; } template FlatGL& FlatGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); return *this; } template FlatGL& FlatGL::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } template FlatGL& FlatGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } @@ -610,7 +688,11 @@ template FlatGL& FlatGL::bindTex "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); return *this; } @@ -619,35 +701,55 @@ template FlatGL& FlatGL::bindTex "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } template FlatGL& FlatGL::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding); return *this; } template FlatGL& FlatGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); return *this; } template FlatGL& FlatGL::bindJointBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, JointBufferBinding); return *this; } template FlatGL& FlatGL::bindJointBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::FlatGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, JointBufferBinding, offset, size); return *this; } #endif @@ -702,8 +804,8 @@ template typename FlatGL::Configuration& Fla "Shaders::FlatGL::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this); CORRADE_ASSERT(secondaryPerVertexCount <= 4, "Shaders::FlatGL::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this); - CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount), - "Shaders::FlatGL::Configuration::setJointCount(): count has to be non-zero iff (secondary) per-vertex joint count is non-zero", *this); + CORRADE_ASSERT(perVertexCount || secondaryPerVertexCount || !count, + "Shaders::FlatGL::Configuration::setJointCount(): count has to be zero if per-vertex joint count is zero", *this); _jointCount = count; _perVertexJointCount = perVertexCount; _secondaryPerVertexJointCount = secondaryPerVertexCount; @@ -723,6 +825,11 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) { `Flag::InstancedObjectId|Flag(0x800)` in the output. */ if(value == FlatGLFlag(UnsignedShort(FlatGLFlag::InstancedObjectId|FlatGLFlag::ObjectIdTexture))) return debug << FlatGLFlag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << FlatGLFlag::ObjectIdTexture; + #ifndef MAGNUM_TARGET_WEBGL + /* Similarly here, both are a superset of UniformBuffers */ + if(value == FlatGLFlag(UnsignedShort(FlatGLFlag::MultiDraw|FlatGLFlag::ShaderStorageBuffers))) + return debug << FlatGLFlag::MultiDraw << Debug::nospace << "|" << Debug::nospace << FlatGLFlag::ShaderStorageBuffers; + #endif #endif debug << "Shaders::FlatGL::Flag" << Debug::nospace; @@ -743,6 +850,9 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) { _c(InstancedTextureOffset) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) _c(TextureArrays) _c(DynamicPerVertexJointCount) @@ -772,7 +882,15 @@ Debug& operator<<(Debug& debug, const FlatGLFlags value) { #endif FlatGLFlag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers; similarly to ObjectId above + letting the Flag printer deal with that */ + FlatGLFlag(UnsignedShort(FlatGLFlag::MultiDraw|FlatGLFlag::ShaderStorageBuffers)), + #endif FlatGLFlag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + FlatGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif FlatGLFlag::UniformBuffers, FlatGLFlag::TextureArrays, FlatGLFlag::DynamicPerVertexJointCount, diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 9845ac82b..1bbd03242 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -60,6 +60,9 @@ namespace Implementation { InstancedTextureOffset = (1 << 7)|TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 UniformBuffers = 1 << 8, + #ifndef MAGNUM_TARGET_WEBGL + ShaderStorageBuffers = UniformBuffers|(1 << 13), + #endif MultiDraw = UniformBuffers|(1 << 9), TextureArrays = 1 << 10, DynamicPerVertexJointCount = 1 << 12 @@ -597,6 +600,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref bindTransformationProjectionBuffer(), * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer() * and @ref bindMaterialBuffer() instead of direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -606,6 +610,24 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: */ UniformBuffers = 1 << 8, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setJointCount(), + * @relativeref{Configuration,setMaterialCount()} and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 13), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the @@ -804,7 +826,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * If @ref Flag::UniformBuffers is set, this is the statically defined * size of the @ref TransformationUniform2D / * @ref TransformationUniform3D uniform buffer bound with - * @ref bindJointBuffer(). + * @ref bindJointBuffer(). Has no use if @ref Flag::ShaderStorageBuffers + * is set. * @see @ref Configuration::setJointCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -843,7 +866,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * * Statically defined size of the @ref FlatMaterialUniform uniform * buffer bound with @ref bindMaterialBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setMaterialCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -860,7 +884,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref TextureTransformationUniform uniform buffers bound with * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and * @ref bindTextureTransformationBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -1097,7 +1122,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -1128,7 +1153,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: FlatGL& setDrawOffset(UnsignedInt offset); /** - * @brief Bind a transformation and projection uniform buffer + * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1149,7 +1174,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: FlatGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1170,7 +1195,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: FlatGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a texture transformation uniform buffer + * @brief Bind a texture transformation uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1190,7 +1215,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: FlatGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1210,7 +1235,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: FlatGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a joint matrix uniform buffer + * @brief Bind a joint matrix uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1413,9 +1438,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: * bound with @ref bindJointBuffer(). Uniform buffers have a statically * defined size and @cpp count*sizeof(TransformationUniform2D) @ce / * @cpp count*sizeof(TransformationUniform3D) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). The - * per-vertex joints then index into the array offset by - * @ref FlatDrawUniform::jointOffset. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-vertex joints index into + * the array offset by @ref FlatDrawUniform::jointOffset. * * The @p perVertexCount and @p secondaryPerVertexCount parameters * describe how many components are taken from @ref JointIds / @@ -1424,8 +1450,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: * @cpp 4 @ce, setting either of these to @cpp 0 @ce means given * attribute is not used at all. If both @p perVertexCount and * @p secondaryPerVertexCount are set to @cpp 0 @ce, skinning is not - * performed; if either of them is non-zero, @p count is expected to be - * non-zero as well. + * performed. Unless @ref Flag::ShaderStorageBuffers is set, if either + * of them is non-zero, @p count is expected to be non-zero as well. * * Default value for all three is @cpp 0 @ce. * @see @ref FlatGL::jointCount(), @ref FlatGL::perVertexJointCount(), @@ -1455,10 +1481,11 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: * @ref FlatMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(FlatMaterialUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref FlatDrawUniform::materialId. Default value is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref FlatDrawUniform::materialId. Default value is + * @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), @@ -1494,10 +1521,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: * @cpp count*sizeof(TransformationProjectionUniform3D) @ce, * @cpp count*sizeof(FlatDrawUniform) @ce and * @cpp count*sizeof(TextureTransformationUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), diff --git a/src/Magnum/Shaders/Line.frag b/src/Magnum/Shaders/Line.frag index c3c833030..a424a7455 100644 --- a/src/Magnum/Shaders/Line.frag +++ b/src/Magnum/Shaders/Line.frag @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + /* See the corresponding block in Line.vert for more information */ #ifndef GL_ES #define CAN_USE_NOPERSPECTIVE @@ -91,11 +95,24 @@ layout(location = 7) uniform highp uint objectId; /* defaults to zero */ #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW -#if DRAW_COUNT > 1 +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -117,11 +134,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; struct MaterialUniform { @@ -134,11 +151,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 3 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -187,7 +204,9 @@ void main() { #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/Line.vert b/src/Magnum/Shaders/Line.vert index a08a19764..562fab884 100644 --- a/src/Magnum/Shaders/Line.vert +++ b/src/Magnum/Shaders/Line.vert @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + /* Use the noperspective keyword to avoid artifacts in screen-space interpolation if perspective projection is used in 3D. If not available, it's worked around by dividing gl_Position with gl_Position.w (which is @@ -112,10 +116,23 @@ uniform mediump float miterLimit #endif ; -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else -#if DRAW_COUNT > 1 +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -129,11 +146,11 @@ uniform highp uint drawOffset #endif layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform TransformationProjection { - highp +) BUFFER_OR_UNIFORM TransformationProjection { + BUFFER_READONLY highp #ifdef TWO_DIMENSIONS /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ @@ -153,11 +170,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; struct MaterialUniform { @@ -170,11 +187,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 3 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -300,7 +317,9 @@ void main() { #else #error #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/LineGL.cpp b/src/Magnum/Shaders/LineGL.cpp index b4f182aa5..8dbb83ed2 100644 --- a/src/Magnum/Shaders/LineGL.cpp +++ b/src/Magnum/Shaders/LineGL.cpp @@ -65,16 +65,32 @@ namespace { } template typename LineGL::CompileState LineGL::compile(const Configuration& configuration) { - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::LineGL: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::LineGL: draw count can't be zero", CompileState{NoCreate}); + #ifndef CORRADE_NO_ASSERT + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::LineGL: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::LineGL: draw count can't be zero", CompileState{NoCreate}); + } + #endif #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::EXT::gpu_shader4); if(configuration.flags() >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif if(configuration.flags() >= Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); @@ -137,12 +153,23 @@ template typename LineGL::CompileState LineG .addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n"_s : ""_s) .addSource(configuration.flags() & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n"_s : ""_s); if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + } vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } vert.addSource(rs.getString("generic.glsl"_s)) @@ -156,12 +183,23 @@ template typename LineGL::CompileState LineG .addSource(configuration.flags() & Flag::ObjectId ? "#define OBJECT_ID\n"_s : ""_s) .addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n"_s : ""_s); if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + frag.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + frag.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + } frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } frag.addSource(rs.getString("generic.glsl"_s)) @@ -225,8 +263,11 @@ template LineGL::LineGL(CompileState&& state { _viewportSizeUniform = uniformLocation("viewportSize"_s); if(_flags >= Flag::UniformBuffers) { - if(_drawCount > 1) - _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else { _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"_s); _widthUniform = uniformLocation("width"_s); @@ -246,7 +287,12 @@ template LineGL::LineGL(CompileState&& state if(state._version < GL::Version::GLES310) #endif { - if(_flags >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(_flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(_flags >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"_s), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"_s), MaterialBufferBinding); @@ -343,51 +389,84 @@ template LineGL& LineGL::setObje template LineGL& LineGL::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= Flag::ShaderStorageBuffers || offset < _drawCount, + "Shaders::LineGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::LineGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + #endif + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || _flags >= Flag::ShaderStorageBuffers + #endif + ) setUniform(_drawOffsetUniform, offset); return *this; } template LineGL& LineGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); return *this; } template LineGL& LineGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); return *this; } template LineGL& LineGL::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } template LineGL& LineGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } template LineGL& LineGL::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding); return *this; } template LineGL& LineGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); return *this; } @@ -399,6 +478,14 @@ template class MAGNUM_SHADERS_EXPORT LineGL<3>; namespace Implementation { Debug& operator<<(Debug& debug, const LineGLFlag value) { + #ifndef MAGNUM_TARGET_WEBGL + /* Special case coming from the Flags printer. As both flags are a superset + of UniformBuffers, printing just one would result in + `Flag::MultiDraw|Flag(0x40)` in the output. */ + if(value == LineGLFlag(UnsignedShort(LineGLFlag::MultiDraw|LineGLFlag::ShaderStorageBuffers))) + return debug << LineGLFlag::MultiDraw << Debug::nospace << "|" << Debug::nospace << LineGLFlag::ShaderStorageBuffers; + #endif + debug << "Shaders::LineGL::Flag" << Debug::nospace; switch(value) { @@ -409,6 +496,9 @@ Debug& operator<<(Debug& debug, const LineGLFlag value) { _c(InstancedObjectId) _c(InstancedTransformation) _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) #undef _c /* LCOV_EXCL_STOP */ @@ -423,7 +513,16 @@ Debug& operator<<(Debug& debug, const LineGLFlags value) { LineGLFlag::InstancedObjectId, /* Superset of ObjectId */ LineGLFlag::ObjectId, LineGLFlag::InstancedTransformation, + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers, meaning printing just one + would result in `Flag::MultiDraw|Flag(0x40)` in the output. So we + pass both and let the Flag printer deal with that. */ + LineGLFlag(UnsignedShort(LineGLFlag::MultiDraw|LineGLFlag::ShaderStorageBuffers)), + #endif LineGLFlag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + LineGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif LineGLFlag::UniformBuffers }); } diff --git a/src/Magnum/Shaders/LineGL.h b/src/Magnum/Shaders/LineGL.h index b481d9485..622738e39 100644 --- a/src/Magnum/Shaders/LineGL.h +++ b/src/Magnum/Shaders/LineGL.h @@ -46,15 +46,14 @@ namespace Magnum { namespace Shaders { namespace Implementation { enum class LineGLFlag: UnsignedShort { VertexColor = 1 << 0, - #ifndef MAGNUM_TARGET_GLES2 ObjectId = 1 << 1, InstancedObjectId = (1 << 2)|ObjectId, - #endif InstancedTransformation = 1 << 3, - #ifndef MAGNUM_TARGET_GLES2 UniformBuffers = 1 << 4, - MultiDraw = UniformBuffers|(1 << 5) + #ifndef MAGNUM_TARGET_WEBGL + ShaderStorageBuffers = UniformBuffers|(1 << 6), #endif + MultiDraw = UniformBuffers|(1 << 5) }; typedef Containers::EnumSet LineGLFlags; CORRADE_ENUMSET_OPERATORS(LineGLFlags) @@ -572,10 +571,28 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: * @ref bindTransformationProjectionBuffer(), * @ref bindDrawBuffer() and @ref bindMaterialBuffer() instead of * direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ UniformBuffers = 1 << 4, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setMaterialCount() and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 6), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the @@ -693,7 +710,8 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: * * Statically defined size of the @ref LineMaterialUniform uniform * buffer bound with @ref bindMaterialBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setMaterialCount() */ UnsignedInt materialCount() const { return _materialCount; } @@ -706,7 +724,7 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: * @ref TransformationProjectionUniform3D and @ref LineDrawUniform * uniform buffers bound with @ref bindTransformationProjectionBuffer() * and @ref bindDrawBuffer(). Has use only if @ref Flag::UniformBuffers - * is set. + * is set and @ref Flag::ShaderStorageBuffers is not set. * @see @ref Configuration::setDrawCount() */ UnsignedInt drawCount() const { return _drawCount; } @@ -877,7 +895,7 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: */ /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -906,7 +924,7 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: LineGL& setDrawOffset(UnsignedInt offset); /** - * @brief Bind a transformation and projection uniform buffer + * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * * Expects that @ref Flag::UniformBuffers is set. The buffer is @@ -920,7 +938,7 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: LineGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */ /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * * Expects that @ref Flag::UniformBuffers is set. The buffer is @@ -934,7 +952,7 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: LineGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */ /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * * Expects that @ref Flag::UniformBuffers is set. The buffer is @@ -1039,10 +1057,11 @@ template class MAGNUM_SHADERS_EXPORT LineGL: * @ref LineMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(LineMaterialUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref LineDrawUniform::materialId. Default value is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref LineDrawUniform::materialId. Default value is + * @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), @@ -1069,10 +1088,10 @@ template class MAGNUM_SHADERS_EXPORT LineGL: * @cpp count*sizeof(TransformationProjectionUniform2D) @ce / * @cpp count*sizeof(TransformationProjectionUniform3D) @ce and * @cpp count*sizeof(LineDrawUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index 5dea76e96..f0a902fc2 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -32,6 +32,10 @@ #define const #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #if defined(WIREFRAME_RENDERING) && defined(GL_ES) && __VERSION__ < 300 #extension GL_OES_standard_derivatives : enable #endif @@ -115,11 +119,24 @@ layout(location = 6) uniform highp uint objectId; /* defaults to zero */ #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW -#if DRAW_COUNT > 1 +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -151,11 +168,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; /* Keep in sync with MeshVisualizer.vert and MeshVisualizer.geom. Can't @@ -175,11 +192,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -261,7 +278,9 @@ void main() { #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index 8c03751d3..1e41e7bc2 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -27,6 +27,10 @@ #define const #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifdef GL_ES #extension GL_EXT_geometry_shader: require #ifdef GL_NV_shader_noperspective_interpolation @@ -84,9 +88,20 @@ uniform lowp float smoothness ; #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) @@ -115,11 +130,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; /* Keep in sync with MeshVisualizer.vert and MeshVisualizer.frag. Can't @@ -139,11 +154,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -292,7 +307,9 @@ void main() { #define drawId drawOffset #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 5cdaec171..d9661b57d 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -31,6 +31,10 @@ #extension GL_ARB_shader_bit_encoding: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -172,10 +176,28 @@ layout(location = PER_INSTANCE_JOINT_COUNT_LOCATION) uniform uint perInstanceJointCount; /* defaults to zero */ #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else -#if DRAW_COUNT > 1 +/* For SSBOs, the per-draw, material and joint arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +/* Define JOINT_COUNT only if there are any per-vertex attributes, otherwise + the buffer would be useless */ +#if defined(PER_VERTEX_JOINT_COUNT) || defined(SECONDARY_PER_VERTEX_JOINT_COUNT) +#define JOINT_COUNT +#endif +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -190,29 +212,29 @@ uniform highp uint drawOffset #ifdef TWO_DIMENSIONS layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform TransformationProjection { +) BUFFER_OR_UNIFORM TransformationProjection { /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ - highp mat3x4 transformationProjectionMatrices[DRAW_COUNT]; + BUFFER_READONLY highp mat3x4 transformationProjectionMatrices[DRAW_COUNT]; }; #elif defined(THREE_DIMENSIONS) layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 0 #endif -) uniform Projection { - highp mat4 projectionMatrix; +) BUFFER_OR_UNIFORM Projection { + BUFFER_READONLY highp mat4 projectionMatrix; }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform Transformation { - highp mat4 transformationMatrices[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Transformation { + BUFFER_READONLY highp mat4 transformationMatrices[DRAW_COUNT]; }; #else #error @@ -227,11 +249,11 @@ struct TextureTransformationUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 3 #endif -) uniform TextureTransformation { - TextureTransformationUniform textureTransformations[DRAW_COUNT]; +) BUFFER_OR_UNIFORM TextureTransformation { + BUFFER_READONLY TextureTransformationUniform textureTransformations[DRAW_COUNT]; }; #endif @@ -252,11 +274,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; /* Keep in sync with MeshVisualizer.geom and MeshVisualizer.frag. Can't @@ -276,20 +298,20 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #ifdef JOINT_COUNT layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 6 #endif -) uniform Joint { - highp +) BUFFER_OR_UNIFORM Joint { + BUFFER_READONLY highp #ifdef TWO_DIMENSIONS /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ @@ -516,7 +538,9 @@ void main() { #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(NORMAL_DIRECTION) mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix); #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index b90bf5f45..5527f6e0d 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -108,6 +108,15 @@ void MeshVisualizerGLBase::assertExtensions(const FlagsBase flags) { if(flags >= FlagBase::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(flags >= FlagBase::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(flags >= FlagBase::MultiDraw) { #ifndef MAGNUM_TARGET_GLES @@ -228,9 +237,15 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra #endif { vert.addSource(Utility::format( - "#define JOINT_COUNT {}\n" - "#define PER_VERTEX_JOINT_COUNT {}u\n" - "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n", + /* SSBOs have an unbounded joints array */ + #ifndef MAGNUM_TARGET_WEBGL + flags >= FlagBase::ShaderStorageBuffers ? + "#define PER_VERTEX_JOINT_COUNT {1}u\n" + "#define SECONDARY_PER_VERTEX_JOINT_COUNT {2}u\n" : + #endif + "#define JOINT_COUNT {}\n" + "#define PER_VERTEX_JOINT_COUNT {}u\n" + "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n", jointCount, perVertexJointCount, secondaryPerVertexJointCount)); @@ -240,7 +255,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra #ifndef MAGNUM_TARGET_WEBGL /* The _LOCATION is needed only if explicit uniform location (desktop / ES3.1) is supported, a plain string can be added otherwise. This is - an immediate uniform also in the UBO case. */ + an immediate uniform also in the UBO / SSBO case. */ #ifndef MAGNUM_TARGET_GLES if(context.isExtensionSupported(version)) #else @@ -260,12 +275,23 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra #endif #ifndef MAGNUM_TARGET_GLES2 if(flags >= FlagBase::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - drawCount, - materialCount)); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(flags >= FlagBase::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + drawCount, + materialCount)); + } vert.addSource(flags >= FlagBase::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -286,12 +312,23 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra ; #ifndef MAGNUM_TARGET_GLES2 if(flags >= FlagBase::UniformBuffers) { - frag.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - drawCount, - materialCount)); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(flags >= FlagBase::ShaderStorageBuffers) { + frag.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + frag.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + drawCount, + materialCount)); + } frag.addSource(flags >= FlagBase::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -403,9 +440,18 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setPerInstanceJointCount(const Unsig MeshVisualizerGLBase& MeshVisualizerGLBase::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= FlagBase::ShaderStorageBuffers || offset < _drawCount, + "Shaders::MeshVisualizerGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::MeshVisualizerGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + #endif + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || _flags >= FlagBase::ShaderStorageBuffers + #endif + ) setUniform(_drawOffsetUniform, offset); return *this; } @@ -414,7 +460,11 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::bindTextureTransformationBuffer(GL:: "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & FlagBase::TextureTransformation, "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= FlagBase::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); return *this; } @@ -423,35 +473,55 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::bindTextureTransformationBuffer(GL:: "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & FlagBase::TextureTransformation, "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= FlagBase::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } MeshVisualizerGLBase& MeshVisualizerGLBase::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= FlagBase::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding); return *this; } MeshVisualizerGLBase& MeshVisualizerGLBase::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= FlagBase::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); return *this; } MeshVisualizerGLBase& MeshVisualizerGLBase::bindJointBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, "Shaders::MeshVisualizerGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= FlagBase::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, JointBufferBinding); return *this; } MeshVisualizerGLBase& MeshVisualizerGLBase::bindJointBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, "Shaders::MeshVisualizerGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= FlagBase::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, JointBufferBinding, offset, size); return *this; } #endif @@ -502,11 +572,18 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(const Configuration /* Has to be here and not in the base class in order to have it exit the constructor when testing for asserts -- GLSL compilation would fail otherwise */ - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::MeshVisualizerGL2D: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::MeshVisualizerGL2D: draw count can't be zero", CompileState{NoCreate}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_NO_ASSERT) + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!configuration.jointCount() == (!configuration.perVertexJointCount() && !configuration.secondaryPerVertexJointCount()), + "Shaders::MeshVisualizerGL2D: joint count can't be zero if per-vertex joint count is non-zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::MeshVisualizerGL2D: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::MeshVisualizerGL2D: draw count can't be zero", CompileState{NoCreate}); + } #endif /* Has to be here and not in the base class in order to have it exit the @@ -585,13 +662,22 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(const Configuration "#define PRIMITIVE_ID\n"_s) : ""_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - geom->addSource(Utility::format( - "#define TWO_DIMENSIONS\n" - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount())); + /* SSBOs have unbounded per-draw arrays so just a plain string can + be passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define TWO_DIMENSIONS\n" + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else { + geom->addSource(Utility::format( + "#define TWO_DIMENSIONS\n" + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + } geom->addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -710,7 +796,11 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D if(flags() >= Flag::DynamicPerVertexJointCount) _perVertexJointCountUniform = uniformLocation("perVertexJointCount"_s); if(flags() >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -761,7 +851,12 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D #ifndef MAGNUM_TARGET_GLES2 if(flags() >= Flag::ObjectIdTexture) setUniform(uniformLocation("objectIdTextureData"_s), ObjectIdTextureUnit); - if(flags() >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(flags() >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(flags() >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"_s), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"_s), MaterialBufferBinding); @@ -886,28 +981,44 @@ MeshVisualizerGL2D& MeshVisualizerGL2D::setJointMatrix(const UnsignedInt id, con MeshVisualizerGL2D& MeshVisualizerGL2D::bindTransformationProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); return *this; } MeshVisualizerGL2D& MeshVisualizerGL2D::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); return *this; } MeshVisualizerGL2D& MeshVisualizerGL2D::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } MeshVisualizerGL2D& MeshVisualizerGL2D::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } #endif @@ -918,8 +1029,8 @@ MeshVisualizerGL2D::Configuration& MeshVisualizerGL2D::Configuration::setJointCo "Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this); CORRADE_ASSERT(secondaryPerVertexCount <= 4, "Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this); - CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount), - "Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): count has to be non-zero iff (secondary) per-vertex joint count is non-zero", *this); + CORRADE_ASSERT(perVertexCount || secondaryPerVertexCount || !count, + "Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): count has to be zero if per-vertex joint count is zero", *this); _jointCount = count; _perVertexJointCount = perVertexCount; _secondaryPerVertexJointCount = secondaryPerVertexCount; @@ -954,11 +1065,21 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration /* Has to be here and not in the base class in order to have it exit the constructor when testing for asserts -- GLSL compilation would fail otherwise */ - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::MeshVisualizerGL3D: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::MeshVisualizerGL3D: draw count can't be zero", CompileState{NoCreate}); + /* Has to be here and not in the base class in order to have it exit the + constructor when testing for asserts -- GLSL compilation would fail + otherwise */ + #if !defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_NO_ASSERT) + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!configuration.jointCount() == (!configuration.perVertexJointCount() && !configuration.secondaryPerVertexJointCount()), + "Shaders::MeshVisualizerGL3D: joint count can't be zero if per-vertex joint count is non-zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::MeshVisualizerGL3D: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::MeshVisualizerGL3D: draw count can't be zero", CompileState{NoCreate}); + } #endif /* Has to be here and not in the base class in order to have it exit the @@ -1069,13 +1190,22 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration .addSource(configuration.flags() & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n"_s : ""_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - geom->addSource(Utility::format( - "#define THREE_DIMENSIONS\n" - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount())); + /* SSBOs have unbounded per-draw arrays so just a plain string can + be passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + geom->addSource( + "#define THREE_DIMENSIONS\n" + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else { + geom->addSource(Utility::format( + "#define THREE_DIMENSIONS\n" + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + } geom->addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -1215,7 +1345,11 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D if(flags() >= Flag::DynamicPerVertexJointCount) _perVertexJointCountUniform = uniformLocation("perVertexJointCount"_s); if(flags() >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -1280,7 +1414,12 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D #ifndef MAGNUM_TARGET_GLES2 if(flags() >= Flag::ObjectIdTexture) setUniform(uniformLocation("objectIdTextureData"_s), ObjectIdTextureUnit); - if(flags() >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(flags() >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(flags() >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("Projection"_s), ProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Transformation"_s), TransformationBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); @@ -1473,42 +1612,66 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setJointMatrix(const UnsignedInt id, con MeshVisualizerGL3D& MeshVisualizerGL3D::bindProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL3D::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, ProjectionBufferBinding); return *this; } MeshVisualizerGL3D& MeshVisualizerGL3D::bindProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL3D::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, ProjectionBufferBinding, offset, size); return *this; } MeshVisualizerGL3D& MeshVisualizerGL3D::bindTransformationBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationBufferBinding); return *this; } MeshVisualizerGL3D& MeshVisualizerGL3D::bindTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationBufferBinding, offset, size); return *this; } MeshVisualizerGL3D& MeshVisualizerGL3D::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } MeshVisualizerGL3D& MeshVisualizerGL3D::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(flags() >= Flag::UniformBuffers, "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + flags() >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } #endif @@ -1519,8 +1682,8 @@ MeshVisualizerGL3D::Configuration& MeshVisualizerGL3D::Configuration::setJointCo "Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this); CORRADE_ASSERT(secondaryPerVertexCount <= 4, "Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this); - CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount), - "Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): count has to be non-zero iff (secondary) per-vertex joint count is non-zero", *this); + CORRADE_ASSERT(perVertexCount || secondaryPerVertexCount || !count, + "Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): count has to be zero if per-vertex joint count is zero", *this); _jointCount = count; _perVertexJointCount = perVertexCount; _secondaryPerVertexJointCount = secondaryPerVertexCount; @@ -1535,6 +1698,11 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { `Flag::InstancedObjectId|Flag(0x4000)` in the output. */ if(value == MeshVisualizerGL2D::Flag(UnsignedInt(MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture))) return debug << MeshVisualizerGL2D::Flag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << MeshVisualizerGL2D::Flag::ObjectIdTexture; + #ifndef MAGNUM_TARGET_WEBGL + /* Similarly here, both are a superset of UniformBuffers */ + if(value == MeshVisualizerGL2D::Flag(UnsignedInt(MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::ShaderStorageBuffers))) + return debug << MeshVisualizerGL2D::Flag::MultiDraw << Debug::nospace << "|" << Debug::nospace << MeshVisualizerGL2D::Flag::ShaderStorageBuffers; + #endif #endif debug << "Shaders::MeshVisualizerGL2D::Flag" << Debug::nospace; @@ -1561,6 +1729,9 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { #endif #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) _c(TextureArrays) _c(DynamicPerVertexJointCount) @@ -1579,6 +1750,11 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { `Flag::InstancedObjectId|Flag(0x4000)` in the output. */ if(value == MeshVisualizerGL3D::Flag(UnsignedInt(MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture))) return debug << MeshVisualizerGL3D::Flag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << MeshVisualizerGL3D::Flag::ObjectIdTexture; + #ifndef MAGNUM_TARGET_WEBGL + /* Similarly here, both are a superset of UniformBuffers */ + if(value == MeshVisualizerGL3D::Flag(UnsignedInt(MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::ShaderStorageBuffers))) + return debug << MeshVisualizerGL3D::Flag::MultiDraw << Debug::nospace << "|" << Debug::nospace << MeshVisualizerGL3D::Flag::ShaderStorageBuffers; + #endif #endif debug << "Shaders::MeshVisualizerGL3D::Flag" << Debug::nospace; @@ -1611,6 +1787,9 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { #endif #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) _c(TextureArrays) _c(DynamicPerVertexJointCount) @@ -1647,7 +1826,15 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) { MeshVisualizerGL2D::Flag::PrimitiveId, #endif #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers; similarly to ObjectId above + letting the Flag printer deal with that */ + MeshVisualizerGL2D::Flag(UnsignedInt(MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::ShaderStorageBuffers)), + #endif MeshVisualizerGL2D::Flag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + MeshVisualizerGL2D::Flag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif MeshVisualizerGL2D::Flag::UniformBuffers, MeshVisualizerGL2D::Flag::TextureArrays, MeshVisualizerGL2D::Flag::DynamicPerVertexJointCount, @@ -1687,7 +1874,15 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flags value) { MeshVisualizerGL3D::Flag::PrimitiveId, #endif #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers; similarly to ObjectId above + letting the Flag printer deal with that */ + MeshVisualizerGL3D::Flag(UnsignedInt(MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::ShaderStorageBuffers)), + #endif MeshVisualizerGL3D::Flag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + MeshVisualizerGL3D::Flag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif MeshVisualizerGL3D::Flag::UniformBuffers, MeshVisualizerGL3D::Flag::TextureArrays, MeshVisualizerGL3D::Flag::DynamicPerVertexJointCount, diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 9b8454172..cd479e49b 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -68,6 +68,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId, /* bit 6, 7, 8, 9 used by 3D-specific TBN visualization */ UniformBuffers = 1 << 10, + ShaderStorageBuffers = UniformBuffers|(1 << 19), MultiDraw = UniformBuffers|(1 << 11), TextureArrays = 1 << 17, DynamicPerVertexJointCount = 1 << 18 @@ -490,6 +491,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * Use uniform buffers. Expects that uniform data are supplied via * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() * and @ref bindMaterialBuffer() instead of direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -499,6 +501,24 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ UniformBuffers = 1 << 10, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setJointCount(), + * @relativeref{Configuration,setMaterialCount()} and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 19), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and combines the value from @ref setDrawOffset() with the @@ -675,7 +695,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * matrices accepted by @ref setJointMatrices() / @ref setJointMatrix(). * If @ref Flag::UniformBuffers is set, this is the statically defined * size of the @ref TransformationUniform2D uniform buffer bound with - * @ref bindJointBuffer(). + * @ref bindJointBuffer(). Has no use if @ref Flag::ShaderStorageBuffers + * is set. * @see @ref Configuration::setJointCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -714,7 +735,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * * Statically defined size of the @ref MeshVisualizerMaterialUniform * uniform buffer bound with @ref bindMaterialBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setMaterialCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -729,7 +751,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @ref TransformationProjectionUniform2D and * @ref MeshVisualizerDrawUniform2D uniform buffers bound with * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer(). - * Has use only if @ref Flag::UniformBuffers is set. + * Has use only if @ref Flag::UniformBuffers is set and + * @ref Flag::ShaderStorageBuffers is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -983,7 +1006,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -1015,7 +1038,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua } /** - * @brief Bind a transformation and projection uniform buffer + * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1035,7 +1058,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua MeshVisualizerGL2D& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1065,7 +1088,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua } /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1090,7 +1113,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua } /** - * @brief Bind a joint matrix uniform buffer + * @brief Bind a joint matrix uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1216,9 +1239,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D::Configuration { * @ref TransformationUniform2D buffer bound with * @ref bindJointBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(TransformationUniform2D) @ce has to be - * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). The - * per-vertex joints then index into the array offset by - * @ref MeshVisualizerDrawUniform2D::jointOffset. + * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-vertex joints index into + * the array offset by @ref MeshVisualizerDrawUniform2D::jointOffset. * * The @p perVertexCount and @p secondaryPerVertexCount parameters * describe how many components are taken from @ref JointIds / @@ -1227,8 +1251,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D::Configuration { * @cpp 4 @ce, setting either of these to @cpp 0 @ce means given * attribute is not used at all. If both @p perVertexCount and * @p secondaryPerVertexCount are set to @cpp 0 @ce, skinning is not - * performed; if either of them is non-zero, @p count is expected to be - * non-zero as well. + * performed. Unless @ref Flag::ShaderStorageBuffers is set, if either + * of them is non-zero, @p count is expected to be non-zero as well. * * Default value for all three is @cpp 0 @ce. * @see @ref MeshVisualizerGL2D::jointCount(), @@ -1259,11 +1283,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D::Configuration { * @ref MeshVisualizerMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(MeshVisualizerMaterialUniform) @ce has to - * be within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref MeshVisualizerDrawUniform2D::materialId. Default value is - * @cpp 1 @ce. + * be within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref MeshVisualizerDrawUniform2D::materialId. Default + * value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), @@ -1297,10 +1321,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D::Configuration { * @cpp count*sizeof(TransformationProjectionUniform2D) @ce, * @cpp count*sizeof(MeshVisualizerDrawUniform2D) @ce and * @cpp count*sizeof(TextureTransformationUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), @@ -2175,6 +2199,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref bindProjectionBuffer(), @ref bindTransformationBuffer(), * @ref bindDrawBuffer() and @ref bindMaterialBuffer() instead of * direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -2184,6 +2209,24 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua */ UniformBuffers = 1 << 10, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setJointCount(), + * @relativeref{Configuration,setMaterialCount()} and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 19), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and combines the value from @ref setDrawOffset() with the @@ -2388,7 +2431,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * matrices accepted by @ref setJointMatrices() / @ref setJointMatrix(). * If @ref Flag::UniformBuffers is set, this is the statically defined * size of the @ref TransformationUniform3D uniform buffer bound with - * @ref bindJointBuffer(). + * @ref bindJointBuffer(). Has no use if @ref Flag::ShaderStorageBuffers + * is set. * @see @ref Configuration::setJointCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -2427,7 +2471,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * * Statically defined size of the @ref MeshVisualizerMaterialUniform * uniform buffer bound with @ref bindMaterialBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setMaterialCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -2442,7 +2487,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref TransformationUniform3D and * @ref MeshVisualizerDrawUniform3D uniform buffers, bound with * @ref bindTransformationBuffer() and @ref bindDrawBuffer(). Has use - * only if @ref Flag::UniformBuffers is set. + * only if @ref Flag::UniformBuffers is set and + * @ref Flag::ShaderStorageBuffers is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -2875,7 +2921,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -2906,7 +2952,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua } /** - * @brief Bind a projection uniform buffer + * @brief Bind a projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -2927,7 +2973,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua MeshVisualizerGL3D& bindProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a transformation uniform buffer + * @brief Bind a transformation uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -2947,7 +2993,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua MeshVisualizerGL3D& bindTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -2968,7 +3014,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua MeshVisualizerGL3D& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a texture transformation uniform buffer for an object ID texture + * @brief Bind a texture transformation uniform / shader storage buffer for an object ID texture * @return Reference to self (for method chaining) * @m_since_latest * @@ -2996,7 +3042,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua } /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -3021,7 +3067,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua } /** - * @brief Bind a joint matrix uniform buffer + * @brief Bind a joint matrix uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -3208,9 +3254,10 @@ class MeshVisualizerGL3D::Configuration { * @ref TransformationUniform3D buffer bound with * @ref bindJointBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(TransformationUniform3D) @ce has to be - * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). The - * per-vertex joints then index into the array offset by - * @ref MeshVisualizerDrawUniform3D::jointOffset. + * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-vertex joints index into + * the array offset by @ref MeshVisualizerDrawUniform3D::jointOffset. * * The @p perVertexCount and @p secondaryPerVertexCount parameters * describe how many components are taken from @ref JointIds / @@ -3219,8 +3266,8 @@ class MeshVisualizerGL3D::Configuration { * @cpp 4 @ce, setting either of these to @cpp 0 @ce means given * attribute is not used at all. If both @p perVertexCount and * @p secondaryPerVertexCount are set to @cpp 0 @ce, skinning is not - * performed; if either of them is non-zero, @p count is expected to be - * non-zero as well. + * performed. Unless @ref Flag::ShaderStorageBuffers is set, if either + * of them is non-zero, @p count is expected to be non-zero as well. * * Default value for all three is @cpp 0 @ce. * @see @ref MeshVisualizerGL2D::jointCount(), @@ -3251,11 +3298,11 @@ class MeshVisualizerGL3D::Configuration { * @ref MeshVisualizerMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(MeshVisualizerMaterialUniform) @ce has to - * be within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref MeshVisualizerDrawUniform3D::materialId. Default value is - * @cpp 1 @ce. + * be within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref MeshVisualizerDrawUniform3D::materialId. Default + * value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), @@ -3289,10 +3336,10 @@ class MeshVisualizerGL3D::Configuration { * @cpp count*sizeof(TransformationUniform3D) @ce, * @cpp count*sizeof(MeshVisualizerDrawUniform3D) @ce and * @cpp count*sizeof(TextureTransformationUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 61a387292..e31b8b483 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifndef NEW_GLSL #define in varying #define fragmentColor gl_FragColor @@ -159,11 +163,25 @@ uniform lowp float lightRanges[LIGHT_COUNT] ; #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw, material and light arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define LIGHT_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW -#if DRAW_COUNT > 1 +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -191,11 +209,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; struct MaterialUniform { @@ -209,14 +227,16 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; -#if LIGHT_COUNT +/* With SSBOs LIGHT_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || LIGHT_COUNT+0 struct LightUniform { highp vec4 position; lowp vec3 colorReserved; @@ -228,11 +248,11 @@ struct LightUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 5 #endif -) uniform Light { - LightUniform lights[LIGHT_COUNT]; +) BUFFER_OR_UNIFORM Light { + BUFFER_READONLY LightUniform lights[LIGHT_COUNT]; }; #endif #endif @@ -364,7 +384,9 @@ void main() { #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 6755236ee..37ce865c4 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -31,6 +31,10 @@ #extension GL_ARB_shader_bit_encoding: require #endif +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -128,10 +132,27 @@ layout(location = PER_INSTANCE_JOINT_COUNT_LOCATION) uniform uint perInstanceJointCount; /* defaults to zero */ #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else -#if DRAW_COUNT > 1 +/* For SSBOs, the per-draw and joint arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +/* Define JOINT_COUNT only if there are any per-vertex attributes, otherwise + the buffer would be useless */ +#if defined(PER_VERTEX_JOINT_COUNT) || defined(SECONDARY_PER_VERTEX_JOINT_COUNT) +#define JOINT_COUNT +#endif +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -169,36 +190,36 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 0 #endif -) uniform Projection { - highp mat4 projectionMatrix; +) BUFFER_OR_UNIFORM Projection { + BUFFER_READONLY highp mat4 projectionMatrix; }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform Transformation { - highp mat4 transformationMatrices[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Transformation { + BUFFER_READONLY highp mat4 transformationMatrices[DRAW_COUNT]; }; #ifdef JOINT_COUNT layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 6 #endif -) uniform Joint { - highp mat4 jointMatrices[JOINT_COUNT]; +) BUFFER_OR_UNIFORM Joint { + BUFFER_READONLY highp mat4 jointMatrices[JOINT_COUNT]; }; #endif @@ -211,11 +232,11 @@ struct TextureTransformationUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 3 #endif -) uniform TextureTransformation { - TextureTransformationUniform textureTransformations[DRAW_COUNT]; +) BUFFER_OR_UNIFORM TextureTransformation { + BUFFER_READONLY TextureTransformationUniform textureTransformations[DRAW_COUNT]; }; #endif #endif diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index e3b6c5c52..8e65da948 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -99,11 +99,22 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { "Shaders::PhongGL: Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead", CompileState{NoCreate}); #endif - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::PhongGL: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::PhongGL: draw count can't be zero", CompileState{NoCreate}); + #ifndef CORRADE_NO_ASSERT + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(configuration.perDrawLightCount() <= configuration.lightCount(), + "Shaders::PhongGL: per-draw light count expected to not be larger than total count of" << configuration.lightCount() << Debug::nospace << ", got" << configuration.perDrawLightCount(), CompileState{NoCreate}); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!configuration.jointCount() == (!configuration.perVertexJointCount() && !configuration.secondaryPerVertexJointCount()), + "Shaders::PhongGL: joint count can't be zero if per-vertex joint count is non-zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::PhongGL: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::PhongGL: draw count can't be zero", CompileState{NoCreate}); + #endif + } #endif #ifndef MAGNUM_TARGET_GLES2 @@ -131,6 +142,15 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { if(configuration.flags() >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES @@ -252,9 +272,15 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { #endif { vert.addSource(Utility::format( - "#define JOINT_COUNT {}\n" - "#define PER_VERTEX_JOINT_COUNT {}u\n" - "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n", + /* SSBOs have an unbounded joints array */ + #ifndef MAGNUM_TARGET_WEBGL + configuration.flags() >= Flag::ShaderStorageBuffers ? + "#define PER_VERTEX_JOINT_COUNT {1}u\n" + "#define SECONDARY_PER_VERTEX_JOINT_COUNT {2}u\n" : + #endif + "#define JOINT_COUNT {}\n" + "#define PER_VERTEX_JOINT_COUNT {1}u\n" + "#define SECONDARY_PER_VERTEX_JOINT_COUNT {2}u\n", configuration.jointCount(), configuration.perVertexJointCount(), configuration.secondaryPerVertexJointCount())); @@ -264,7 +290,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { #ifndef MAGNUM_TARGET_WEBGL /* The _LOCATION is needed only if explicit uniform location (desktop / ES3.1) is supported, a plain string can be added otherwise. This is - an immediate uniform also in the UBO case. */ + an immediate uniform also in the UBO / SSBO case. */ #ifndef MAGNUM_TARGET_GLES if(context.isExtensionSupported(version)) #else @@ -284,10 +310,21 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - configuration.drawCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + configuration.drawCount())); + } vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -316,15 +353,22 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { frag.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n" - "#define LIGHT_COUNT {}\n" - "#define PER_DRAW_LIGHT_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount(), - configuration.lightCount(), - configuration.perDrawLightCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw, material and light arrays */ + configuration.flags() >= Flag::ShaderStorageBuffers ? + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n" + "#define PER_DRAW_LIGHT_COUNT {3}\n" : + #endif + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {0}\n" + "#define MATERIAL_COUNT {1}\n" + "#define LIGHT_COUNT {2}\n" + "#define PER_DRAW_LIGHT_COUNT {3}\n", + configuration.drawCount(), + configuration.materialCount(), + configuration.lightCount(), + configuration.perDrawLightCount())); frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s) .addSource(configuration.flags() >= Flag::LightCulling ? "#define LIGHT_CULLING\n"_s : ""_s); } else @@ -469,7 +513,11 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast(std::move if(_flags >= Flag::DynamicPerVertexJointCount) _perVertexJointCountUniform = uniformLocation("perVertexJointCount"_s); if(_flags >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -524,7 +572,12 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast(std::move } #ifndef MAGNUM_TARGET_GLES2 if(_flags >= Flag::ObjectIdTexture) setUniform(uniformLocation("objectIdTextureData"_s), ObjectIdTextureUnit); - if(_flags >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(_flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(_flags >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("Projection"_s), ProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Transformation"_s), TransformationBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); @@ -940,51 +993,84 @@ PhongGL& PhongGL::setPerInstanceJointCount(const UnsignedInt count) { PhongGL& PhongGL::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= Flag::ShaderStorageBuffers || offset < _drawCount, + "Shaders::PhongGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::PhongGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + #endif + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || _flags >= Flag::ShaderStorageBuffers + #endif + ) setUniform(_drawOffsetUniform, offset); return *this; } PhongGL& PhongGL::bindProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, ProjectionBufferBinding); return *this; } PhongGL& PhongGL::bindProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, ProjectionBufferBinding, offset, size); return *this; } PhongGL& PhongGL::bindTransformationBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationBufferBinding); return *this; } PhongGL& PhongGL::bindTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationBufferBinding, offset, size); return *this; } PhongGL& PhongGL::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } PhongGL& PhongGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } @@ -993,7 +1079,11 @@ PhongGL& PhongGL::bindTextureTransformationBuffer(GL::Buffer& buffer) { "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); return *this; } @@ -1002,49 +1092,77 @@ PhongGL& PhongGL::bindTextureTransformationBuffer(GL::Buffer& buffer, const GLin "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } PhongGL& PhongGL::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding); return *this; } PhongGL& PhongGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); return *this; } PhongGL& PhongGL::bindLightBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindLightBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, LightBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, LightBufferBinding); return *this; } PhongGL& PhongGL::bindLightBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindLightBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, LightBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, LightBufferBinding, offset, size); return *this; } PhongGL& PhongGL::bindJointBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, JointBufferBinding); return *this; } PhongGL& PhongGL::bindJointBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::PhongGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, JointBufferBinding, offset, size); return *this; } #endif @@ -1171,10 +1289,8 @@ PhongGL& PhongGL::bindTextures(GL::Texture2D* ambient, GL::Texture2D* diffuse, G } PhongGL::Configuration& PhongGL::Configuration::setLightCount(const UnsignedInt count, const UnsignedInt perDrawCount) { - CORRADE_ASSERT(!count == !perDrawCount, - "Shaders::PhongGL::Configuration::setLightCount(): count has to be non-zero iff per-draw count is non-zero", *this); - CORRADE_ASSERT(perDrawCount <= count, - "Shaders::PhongGL::Configuration::setLightCount(): per-draw light count expected to be not larger than total count of" << count << Debug::nospace << ", got" << perDrawCount, *this); + CORRADE_ASSERT(perDrawCount || !count, + "Shaders::PhongGL::Configuration::setLightCount(): count has to be zero if per-draw count is zero", *this); _lightCount = count; _perDrawLightCount = perDrawCount; return *this; @@ -1186,8 +1302,8 @@ PhongGL::Configuration& PhongGL::Configuration::setJointCount(UnsignedInt count, "Shaders::PhongGL::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this); CORRADE_ASSERT(secondaryPerVertexCount <= 4, "Shaders::PhongGL::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this); - CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount), - "Shaders::PhongGL::Configuration::setJointCount(): count has to be non-zero iff (secondary) per-vertex joint count is non-zero", *this); + CORRADE_ASSERT(perVertexCount || secondaryPerVertexCount || !count, + "Shaders::PhongGL::Configuration::setJointCount(): count has to be zero if per-vertex joint count is zero", *this); _jointCount = count; _perVertexJointCount = perVertexCount; _secondaryPerVertexJointCount = secondaryPerVertexCount; @@ -1202,6 +1318,11 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { `Flag::InstancedObjectId|Flag(0x20000)` in the output. */ if(value == PhongGL::Flag(UnsignedInt(PhongGL::Flag::InstancedObjectId|PhongGL::Flag::ObjectIdTexture))) return debug << PhongGL::Flag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << PhongGL::Flag::ObjectIdTexture; + #ifndef MAGNUM_TARGET_WEBGL + /* Similarly here, both are a superset of UniformBuffers */ + if(value == PhongGL::Flag(UnsignedInt(PhongGL::Flag::MultiDraw|PhongGL::Flag::ShaderStorageBuffers))) + return debug << PhongGL::Flag::MultiDraw << Debug::nospace << "|" << Debug::nospace << PhongGL::Flag::ShaderStorageBuffers; + #endif #endif debug << "Shaders::PhongGL::Flag" << Debug::nospace; @@ -1226,6 +1347,9 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { _c(InstancedTextureOffset) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) _c(TextureArrays) _c(LightCulling) @@ -1263,7 +1387,15 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { #endif PhongGL::Flag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers; similarly to ObjectId above + letting the Flag printer deal with that */ + PhongGL::Flag(UnsignedInt(PhongGL::Flag::MultiDraw|PhongGL::Flag::ShaderStorageBuffers)), + #endif PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + PhongGL::Flag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif PhongGL::Flag::UniformBuffers, PhongGL::Flag::TextureArrays, PhongGL::Flag::LightCulling, diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 7a88116dc..3c6935813 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -789,6 +789,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer(), * @ref bindMaterialBuffer() and @ref bindLightBuffer() instead of * direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -798,6 +799,25 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { */ UniformBuffers = 1 << 12, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setLightCount(), + * @relativeref{Configuration,setJointCount()}, + * @relativeref{Configuration,setMaterialCount()} and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 19), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the @@ -1032,7 +1052,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * If @ref Flag::UniformBuffers is set, this is the statically defined * size of the @ref PhongLightUniform uniform buffer bound with - * @ref bindLightBuffer(). + * @ref bindLightBuffer(). Has no use if @ref Flag::ShaderStorageBuffers + * is set. * @see @ref perDrawLightCount(), @ref Configuration::setLightCount() */ UnsignedInt lightCount() const { return _lightCount; } @@ -1060,7 +1081,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * matrices accepted by @ref setJointMatrices() / @ref setJointMatrix(). * If @ref Flag::UniformBuffers is set, this is the statically defined * size of the @ref TransformationUniform3D uniform buffer bound with - * @ref bindJointBuffer(). + * @ref bindJointBuffer(). Has no use if @ref Flag::ShaderStorageBuffers + * is set. * @see @ref Configuration::setJointCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -1099,7 +1121,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * Statically defined size of the @ref PhongMaterialUniform uniform * buffer bound with @ref bindMaterialBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setMaterialCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -1114,7 +1137,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @ref PhongDrawUniform and @ref TextureTransformationUniform uniform * buffers bound with @ref bindTransformationBuffer(), * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer(). - * Has use only if @ref Flag::UniformBuffers is set. + * Has use only if @ref Flag::UniformBuffers is set and + * @ref Flag::ShaderStorageBuffers is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -1691,7 +1715,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -1721,7 +1745,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& setDrawOffset(UnsignedInt offset); /** - * @brief Bind a projection uniform buffer + * @brief Bind a projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1742,7 +1766,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a transformation uniform buffer + * @brief Bind a transformation uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1763,7 +1787,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1784,7 +1808,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a texture transformation uniform buffer + * @brief Bind a texture transformation uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1804,7 +1828,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1825,7 +1849,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a light uniform buffer + * @brief Bind a light uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -1844,7 +1868,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindLightBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a joint matrix uniform buffer + * @brief Bind a joint matrix uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -2169,15 +2193,18 @@ class MAGNUM_SHADERS_EXPORT PhongGL::Configuration { * @ref PhongLightUniform buffer bound with @ref bindLightBuffer(). * Uniform buffers have a statically defined size and * @cpp count*sizeof(PhongLightUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). The per-draw - * lights are then specified via @ref PhongDrawUniform::lightOffset and + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw lights are specified + * via @ref PhongDrawUniform::lightOffset and * @ref PhongDrawUniform::lightCount. * * The @p perDrawCount parameter describes how many lights out of * @p count get applied to each draw. Useful mainly in combination with * @ref Flag::LightCulling, without it can be used for conveniently * reducing the light count without having to reduce sizes of the light - * arrays as well. It's expected to not be larger than @p count. If set + * arrays as well. Unless @ref Flag::ShaderStorageBuffers is set, + * @p perDrawCount is expected to not be larger than @p count. If set * to @cpp 0 @ce, no lighting calculations are performed and only the * ambient contribution to the color is used. If @p perDrawCount is * @cpp 0 @ce, @p count is expected to be zero as well. @@ -2234,9 +2261,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL::Configuration { * @ref TransformationUniform3D buffer bound with * @ref bindJointBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(TransformationUniform3D) @ce has to be - * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). The - * per-vertex joints then index into the array offset by - * @ref PhongDrawUniform::jointOffset. + * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-vertex joints index into + * the array offset by @ref PhongDrawUniform::jointOffset. * * The @p perVertexCount and @p secondaryPerVertexCount parameters * describe how many components are taken from @ref JointIds / @@ -2245,8 +2273,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL::Configuration { * @cpp 4 @ce, setting either of these to @cpp 0 @ce means given * attribute is not used at all. If both @p perVertexCount and * @p secondaryPerVertexCount are set to @cpp 0 @ce, skinning is not - * performed; if either of them is non-zero, @p count is expected to be - * non-zero as well. + * performed. Unless @ref Flag::ShaderStorageBuffers is set, if either + * of them is non-zero, @p count is expected to be non-zero as well. * * Default value for all three is @cpp 0 @ce. * @see @ref PhongGL::jointCount(), @ref PhongGL::perVertexJointCount(), @@ -2276,10 +2304,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL::Configuration { * @ref PhongMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(PhongMaterialUniform) @ce has to be - * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref PhongDrawUniform::materialId. Default value is @cpp 1 @ce. + * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref PhongDrawUniform::materialId. Default value is + * @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setLightCount(), @ref setDrawCount(), @@ -2313,10 +2342,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL::Configuration { * @cpp count*sizeof(TransformationUniform3D) @ce, * @cpp count*sizeof(PhongDrawUniform) @ce and * @cpp count*sizeof(TextureTransformationUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setLightCount(), @ref setMaterialCount(), diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 0f5f08658..caaca7036 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -187,7 +187,10 @@ 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} + {"multidraw with all the things", DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation, 16, 48}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with all the things", DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation, 0, 0} + #endif }; constexpr struct { @@ -196,6 +199,7 @@ constexpr struct { UnsignedInt materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData[]{ + /* These two fail for UBOs but not SSBOs */ {"zero draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 1, 0, "draw count can't be zero"}, {"zero materials", DistanceFieldVectorGL2D::Flag::UniformBuffers, 0, 1, @@ -232,21 +236,40 @@ constexpr struct { const char* expected3D; DistanceFieldVectorGL2D::Flags flags; UnsignedInt materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - {}, 1, 1, 16, + {}, 1, 1, true, 16, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, shader storage", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers, 0, 0, true, 16, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, + #endif {"draw offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - {}, 2, 3, 1, + {}, 2, 3, false, 1, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, shader storage", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers, 0, 0, false, 1, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, + #endif {"multidraw", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - DistanceFieldVectorGL2D::Flag::MultiDraw, 2, 3, 1, + DistanceFieldVectorGL2D::Flag::MultiDraw, 2, 3, false, 1, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, shader storage", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL2D::Flag::MultiDraw, 0, 0, false, 1, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, + #endif }; #endif @@ -312,10 +335,16 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { &DistanceFieldVectorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &DistanceFieldVectorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_WEBGL + &DistanceFieldVectorGLTest::renderDefaults2D, + #endif #endif &DistanceFieldVectorGLTest::renderDefaults3D, #ifndef MAGNUM_TARGET_GLES2 &DistanceFieldVectorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_WEBGL + &DistanceFieldVectorGLTest::renderDefaults3D, + #endif #endif }, &DistanceFieldVectorGLTest::renderSetup, @@ -326,10 +355,16 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { &DistanceFieldVectorGLTest::render2D, #ifndef MAGNUM_TARGET_GLES2 &DistanceFieldVectorGLTest::render2D, + #ifndef MAGNUM_TARGET_WEBGL + &DistanceFieldVectorGLTest::render2D, + #endif #endif &DistanceFieldVectorGLTest::render3D, #ifndef MAGNUM_TARGET_GLES2 &DistanceFieldVectorGLTest::render3D, + #ifndef MAGNUM_TARGET_WEBGL + &DistanceFieldVectorGLTest::render3D, + #endif #endif }, Containers::arraySize(RenderData), @@ -424,6 +459,18 @@ template void DistanceFieldVectorGLTest::constructUnifor CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -736,6 +783,19 @@ constexpr GL::TextureFormat TextureFormatR = template void DistanceFieldVectorGLTest::renderDefaults2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -779,7 +839,12 @@ template void DistanceFieldVectorGLTest::ren shader.draw(square); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { + else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -825,6 +890,19 @@ template void DistanceFieldVectorGLTest::ren template void DistanceFieldVectorGLTest::renderDefaults3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -868,7 +946,12 @@ template void DistanceFieldVectorGLTest::ren shader.draw(plane); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { + else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -917,6 +1000,19 @@ template void DistanceFieldVectorGLTest::ren setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -968,7 +1064,12 @@ template void DistanceFieldVectorGLTest::ren .draw(square); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { + else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -1026,6 +1127,19 @@ template void DistanceFieldVectorGLTest::ren setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1080,7 +1194,12 @@ template void DistanceFieldVectorGLTest::ren .draw(plane); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { + else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -1146,6 +1265,18 @@ void DistanceFieldVectorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1252,11 +1383,11 @@ void DistanceFieldVectorGLTest::renderMulti2D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1); + .setMaterialId(data.bindWithOffset ? 0 : 1); drawData[2*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Configuration{} @@ -1265,8 +1396,8 @@ void DistanceFieldVectorGLTest::renderMulti2D() { .setDrawCount(data.drawCount)}; shader.bindVectorTexture(vector); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 0*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), sizeof(DistanceFieldVectorMaterialUniform)); @@ -1350,6 +1481,18 @@ void DistanceFieldVectorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= DistanceFieldVectorGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1461,11 +1604,11 @@ void DistanceFieldVectorGLTest::renderMulti3D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1); + .setMaterialId(data.bindWithOffset ? 0 : 1); drawData[2*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Configuration{} @@ -1474,8 +1617,8 @@ void DistanceFieldVectorGLTest::renderMulti3D() { .setDrawCount(data.drawCount)}; shader.bindVectorTexture(vector); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 0*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), sizeof(DistanceFieldVectorMaterialUniform)); diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp index ad8020b4b..1ae12ed7b 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp @@ -99,10 +99,24 @@ void DistanceFieldVectorGL_Test::debugFlags() { #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"); + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ + { + std::ostringstream out; + Debug{&out} << (DistanceFieldVectorGL3D::Flag::MultiDraw|DistanceFieldVectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::MultiDraw\n"); + } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (DistanceFieldVectorGL3D::Flag::MultiDraw|DistanceFieldVectorGL3D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::MultiDraw|Shaders::DistanceFieldVectorGL::Flag::ShaderStorageBuffers\n"); + } + #endif } #endif diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index ef67dbdbe..1e86d770c 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -318,7 +318,13 @@ constexpr struct { {"multidraw with all the things except secondary per-vertex sets", FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId|FlatGL2D::Flag::DynamicPerVertexJointCount, 8, 48, 16, 4, 0}, {"multidraw with all the things except instancing", FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::DynamicPerVertexJointCount, - 8, 48, 16, 3, 4} + 8, 48, 16, 3, 4}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with all the things except secondary per-vertex sets", FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId|FlatGL2D::Flag::DynamicPerVertexJointCount, + 0, 0, 0, 4, 0}, + {"shader storage + multidraw with all the things except instancing", FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::DynamicPerVertexJointCount, + 0, 0, 0, 3, 4} + #endif }; #endif @@ -360,15 +366,30 @@ constexpr struct { constexpr struct { const char* name; FlatGL2D::Flags flags; - UnsignedInt materialCount, drawCount; + UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount, materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData[]{ - {"zero draws", FlatGL2D::Flag::UniformBuffers, 1, 0, + /* These two fail for UBOs but not SSBOs */ + {"zero draws", + FlatGL2D::Flag::UniformBuffers, + 0, 0, 0, 1, 0, "draw count can't be zero"}, - {"zero materials", FlatGL2D::Flag::UniformBuffers, 0, 1, + {"zero materials", + FlatGL2D::Flag::UniformBuffers, + 0, 0, 0, 0, 1, "material count can't be zero"}, - {"texture arrays but no transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, 1, 1, - "texture arrays require texture transformation enabled as well if uniform buffers are used"} + {"texture arrays but no transformation", + FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 0, 0, 0, 1, 1, + "texture arrays require texture transformation enabled as well if uniform buffers are used"}, + /* These two fail for UBOs but not SSBOs */ + {"per-vertex joint count but no joint count", + FlatGL2D::Flag::UniformBuffers, + 0, 2, 0, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, + {"secondary per-vertex joint count but no joint count", FlatGL2D::Flag::UniformBuffers, + 0, 0, 3, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, }; #endif @@ -663,94 +684,123 @@ constexpr struct { UnsignedInt expectedId[3]; FlatGL2D::Flags flags; UnsignedInt materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset, colored", "multidraw2D.tga", "multidraw3D.tga", {}, - {}, 1, 1, 16, 0.0f, 0.0f}, + {}, 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, colored + object ID", "multidraw2D.tga", "multidraw3D.tga", {1211, 5627, 36363}, FlatGL2D::Flag::ObjectId, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, colored + textured object ID", "multidraw2D.tga", "multidraw3D.tga", {3211, 8627, 40363}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::ObjectIdTexture, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, colored + textured array object ID", "multidraw2D.tga", "multidraw3D.tga", {3211, 8627, 40363}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::ObjectIdTexture|FlatGL2D::Flag::TextureArrays, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 1, 1, 16, + 1, 1, true, 16, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"bind with offset, texture array", "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, - 1, 1, 16, + 1, 1, true, 16, /* Some difference at the UV edge (texture is wrapping in the 2D case while the 2D array has a black area around) */ 65.0f, 0.15f}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, texture array, shader storage", + "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, + FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 0, 0, true, 16, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 65.0f, 0.15f}, + #endif {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", {}, {}, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, colored + object ID", "multidraw2D.tga", "multidraw3D.tga", {1211, 5627, 36363}, FlatGL2D::Flag::ObjectId, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, colored + textured object ID", "multidraw2D.tga", "multidraw3D.tga", {3211, 8627, 40363}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::ObjectIdTexture, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, colored + textured array object ID", "multidraw2D.tga", "multidraw3D.tga", {3211, 8627, 40363}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::ObjectIdTexture|FlatGL2D::Flag::TextureArrays, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"draw offset, texture array", "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, - 2, 3, 1, + 2, 3, false, 1, /* Some difference at the UV edge (texture is wrapping in the 2D case while the 2D array has a black area around) */ 65.0f, 0.15f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, texture array, shader storage", + "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, + FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 0, 0, false, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 65.0f, 0.15f}, + #endif {"multidraw, colored", "multidraw2D.tga", "multidraw3D.tga", {}, - FlatGL2D::Flag::MultiDraw, 2, 3, 1, 0.0f, 0.0f}, + FlatGL2D::Flag::MultiDraw, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, colored + object ID", "multidraw2D.tga", "multidraw3D.tga", {1211, 5627, 36363}, FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::ObjectId, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, colored + textured object ID", "multidraw2D.tga", "multidraw3D.tga", {3211, 8627, 40363}, FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::ObjectIdTexture, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, colored + textured array object ID", "multidraw2D.tga", "multidraw3D.tga", {3211, 8627, 40363}, FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::ObjectIdTexture|FlatGL2D::Flag::TextureArrays, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"multidraw, texture array", "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, - 2, 3, 1, + 2, 3, false, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 65.0f, 0.15f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, texture array, shader storage", + "multidraw-textured2D.tga", "multidraw-textured3D.tga", {}, + FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 0, 0, false, 1, /* Some difference at the UV edge (texture is wrapping in the 2D case while the 2D array has a black area around) */ 65.0f, 0.15f} + #endif }; /* Same as in PhongGL and MeshVisualizerGL tests */ @@ -758,14 +808,33 @@ const struct { const char* name; FlatGL2D::Flags flags; UnsignedInt materialCount, drawCount, jointCount; + bool bindWithOffset; UnsignedInt uniformIncrement; } RenderMultiSkinningData[]{ {"bind with offset", - {}, 1, 1, 4, 16}, + {}, + 1, 1, 4, true, 16}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, shader storage", + FlatGL2D::Flag::ShaderStorageBuffers, + 0, 0, 0, true, 16}, + #endif {"draw offset", - {}, 2, 3, 9, 1}, + {}, + 2, 3, 9, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, shader storage", + FlatGL2D::Flag::ShaderStorageBuffers, + 0, 0, 0, false, 1}, + #endif {"multidraw", - FlatGL2D::Flag::MultiDraw, 2, 3, 9, 1} + FlatGL2D::Flag::MultiDraw, + 2, 3, 9, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, shader storage", + FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::MultiDraw, + 0, 0, 0, false, 1} + #endif }; #endif @@ -872,18 +941,30 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderDefaults2D, + #endif #endif &FlatGLTest::renderDefaults3D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderDefaults3D, + #endif #endif &FlatGLTest::renderColored2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderColored2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderColored2D, + #endif #endif &FlatGLTest::renderColored3D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderColored3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderColored3D, + #endif #endif }, &FlatGLTest::renderSetup, @@ -894,10 +975,16 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderSinglePixelTextured2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderSinglePixelTextured2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderSinglePixelTextured2D, + #endif #endif &FlatGLTest::renderSinglePixelTextured3D, #ifndef MAGNUM_TARGET_GLES2 - &FlatGLTest::renderSinglePixelTextured3D + &FlatGLTest::renderSinglePixelTextured3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderSinglePixelTextured3D + #endif #endif }, Containers::arraySize(RenderSinglePixelTexturedData), @@ -909,10 +996,16 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderTextured2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderTextured2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderTextured2D, + #endif #endif &FlatGLTest::renderTextured3D, #ifndef MAGNUM_TARGET_GLES2 - &FlatGLTest::renderTextured3D + &FlatGLTest::renderTextured3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderTextured3D + #endif #endif }, Containers::arraySize(RenderTexturedData), @@ -924,18 +1017,30 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderVertexColor2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderVertexColor2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderVertexColor2D, + #endif #endif &FlatGLTest::renderVertexColor2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderVertexColor2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderVertexColor2D, + #endif #endif &FlatGLTest::renderVertexColor3D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderVertexColor3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderVertexColor3D, + #endif #endif &FlatGLTest::renderVertexColor3D, #ifndef MAGNUM_TARGET_GLES2 - &FlatGLTest::renderVertexColor3D + &FlatGLTest::renderVertexColor3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderVertexColor3D + #endif #endif }, &FlatGLTest::renderSetup, @@ -946,10 +1051,16 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderAlpha2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderAlpha2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderAlpha2D, + #endif #endif &FlatGLTest::renderAlpha3D, #ifndef MAGNUM_TARGET_GLES2 - &FlatGLTest::renderAlpha3D + &FlatGLTest::renderAlpha3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderAlpha3D, + #endif #endif }, Containers::arraySize(RenderAlphaData), @@ -961,8 +1072,15 @@ FlatGLTest::FlatGLTest() { addInstancedTests({ &FlatGLTest::renderObjectId2D, &FlatGLTest::renderObjectId2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderObjectId2D, + #endif &FlatGLTest::renderObjectId3D, - &FlatGLTest::renderObjectId3D}, + &FlatGLTest::renderObjectId3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderObjectId3D, + #endif + }, Containers::arraySize(RenderObjectIdData), &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); @@ -973,8 +1091,15 @@ FlatGLTest::FlatGLTest() { addInstancedTests({ &FlatGLTest::renderSkinning2D, &FlatGLTest::renderSkinning2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderSkinning2D, + #endif &FlatGLTest::renderSkinning3D, - &FlatGLTest::renderSkinning3D}, + &FlatGLTest::renderSkinning3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderSkinning3D, + #endif + }, Containers::arraySize(RenderSkinningData), &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); @@ -985,10 +1110,16 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderInstanced2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderInstanced2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderInstanced2D, + #endif #endif &FlatGLTest::renderInstanced3D, #ifndef MAGNUM_TARGET_GLES2 - &FlatGLTest::renderInstanced3D + &FlatGLTest::renderInstanced3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderInstanced3D + #endif #endif }, Containers::arraySize(RenderInstancedData), @@ -1000,8 +1131,15 @@ FlatGLTest::FlatGLTest() { addTests({ &FlatGLTest::renderInstancedSkinning2D, &FlatGLTest::renderInstancedSkinning2D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderInstancedSkinning2D, + #endif &FlatGLTest::renderInstancedSkinning3D, - &FlatGLTest::renderInstancedSkinning3D}, + &FlatGLTest::renderInstancedSkinning3D, + #ifndef MAGNUM_TARGET_WEBGL + &FlatGLTest::renderInstancedSkinning3D + #endif + }, &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); #endif @@ -1143,6 +1281,18 @@ template void FlatGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= FlatGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= FlatGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1328,6 +1478,7 @@ template void FlatGLTest::constructUniformBuffersInvalid Error redirectError{&out}; FlatGL{typename FlatGL::Configuration{} .setFlags(data.flags) + .setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount) .setMaterialCount(data.materialCount) .setDrawCount(data.drawCount)}; CORRADE_COMPARE(out.str(), Utility::formatString( @@ -1675,6 +1826,19 @@ void FlatGLTest::renderTeardown() { template void FlatGLTest::renderDefaults2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1694,7 +1858,12 @@ template void FlatGLTest::renderDefaults2D() { shader.draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -1729,6 +1898,19 @@ template void FlatGLTest::renderDefaults2D() { template void FlatGLTest::renderDefaults3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1748,7 +1930,12 @@ template void FlatGLTest::renderDefaults3D() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL3D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -1783,6 +1970,19 @@ template void FlatGLTest::renderDefaults3D() { template void FlatGLTest::renderColored2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1805,7 +2005,12 @@ template void FlatGLTest::renderColored2D() { .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -1847,6 +2052,19 @@ template void FlatGLTest::renderColored2D() { template void FlatGLTest::renderColored3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1873,7 +2091,12 @@ template void FlatGLTest::renderColored3D() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL3D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -1939,6 +2162,19 @@ template void FlatGLTest::renderSinglePixelTextured2D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1959,7 +2195,7 @@ template void FlatGLTest::renderSinglePixelTextured2D() { FlatGL2D::Flags flags = FlatGL2D::Flag::Textured|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { + if(flag & FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= FlatGL2D::Flag::TextureTransformation; } @@ -1981,7 +2217,7 @@ template void FlatGLTest::renderSinglePixelTextured2D() { .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindTexture(textureArray); - if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & FlatGL2D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else #endif @@ -2000,7 +2236,12 @@ template void FlatGLTest::renderSinglePixelTextured2D() { .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -2052,6 +2293,19 @@ template void FlatGLTest::renderSinglePixelTextured3D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2072,7 +2326,7 @@ template void FlatGLTest::renderSinglePixelTextured3D() { FlatGL3D::Flags flags = FlatGL2D::Flag::Textured|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == FlatGL3D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { + if(flag & FlatGL3D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= FlatGL3D::Flag::TextureTransformation; } @@ -2094,7 +2348,7 @@ template void FlatGLTest::renderSinglePixelTextured3D() { .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindTexture(textureArray); - if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & FlatGL2D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else #endif @@ -2118,7 +2372,12 @@ template void FlatGLTest::renderSinglePixelTextured3D() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL3D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -2175,6 +2434,19 @@ template void FlatGLTest::renderTextured2D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2205,7 +2477,7 @@ template void FlatGLTest::renderTextured2D() { FlatGL2D::Flags flags = data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { + if(flag & FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= FlatGL2D::Flag::TextureTransformation; } @@ -2224,7 +2496,7 @@ template void FlatGLTest::renderTextured2D() { .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); shader.bindTexture(textureArray); - if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & FlatGL2D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else #endif @@ -2248,7 +2520,12 @@ template void FlatGLTest::renderTextured2D() { .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -2302,6 +2579,19 @@ template void FlatGLTest::renderTextured3D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2332,7 +2622,7 @@ template void FlatGLTest::renderTextured3D() { FlatGL3D::Flags flags = data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { + if(flag & FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= FlatGL3D::Flag::TextureTransformation; } @@ -2351,7 +2641,7 @@ template void FlatGLTest::renderTextured3D() { .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); shader.bindTexture(textureArray); - if(flag != FlatGL3D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & FlatGL3D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else #endif @@ -2380,7 +2670,12 @@ template void FlatGLTest::renderTextured3D() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL3D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -2436,6 +2731,19 @@ template void FlatGLTest::renderTextured3D() { template void FlatGLTest::renderVertexColor2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -2488,7 +2796,12 @@ template void FlatGLTest::renderVertexColor2D() { .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -2526,6 +2839,19 @@ template void FlatGLTest::renderVertexColor2D() { template void FlatGLTest::renderVertexColor3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -2582,7 +2908,12 @@ template void FlatGLTest::renderVertexColor3D() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -2642,6 +2973,19 @@ template void FlatGLTest::renderAlpha2D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2687,7 +3031,12 @@ template void FlatGLTest::renderAlpha2D() { .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -2729,6 +3078,19 @@ template void FlatGLTest::renderAlpha3D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2784,7 +3146,12 @@ template void FlatGLTest::renderAlpha3D() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL3D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -2837,6 +3204,19 @@ template void FlatGLTest::renderObjectId2D() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2864,7 +3244,7 @@ template void FlatGLTest::renderObjectId2D() { GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, circleFlags)); FlatGL2D::Flags flags = data.flags|flag; - if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { + if(flag & FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= FlatGL2D::Flag::TextureTransformation; } @@ -2887,7 +3267,7 @@ template void FlatGLTest::renderObjectId2D() { .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindObjectIdTexture(textureArray); - if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & FlatGL2D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else { texture = GL::Texture2D{}; @@ -2917,7 +3297,12 @@ template void FlatGLTest::renderObjectId2D() { .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) .setObjectId(40006) .draw(circle); - } else if(flag == FlatGL2D::Flag::UniformBuffers) { + } else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -2980,6 +3365,19 @@ template void FlatGLTest::renderObjectId3D() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3007,7 +3405,7 @@ template void FlatGLTest::renderObjectId3D() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, sphereFlags)); FlatGL3D::Flags flags = data.flags|flag; - if(flag == FlatGL3D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { + if(flag & FlatGL3D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= FlatGL3D::Flag::TextureTransformation; } @@ -3030,7 +3428,7 @@ template void FlatGLTest::renderObjectId3D() { .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindObjectIdTexture(textureArray); - if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & FlatGL2D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else { texture = GL::Texture2D{}; @@ -3064,7 +3462,12 @@ template void FlatGLTest::renderObjectId3D() { Matrix4::rotationX(15.0_degf)) .setObjectId(40006) .draw(sphere); - } else if(flag == FlatGL3D::Flag::UniformBuffers) { + } else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -3149,6 +3552,19 @@ template void FlatGLTest::renderSkinning2D() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3223,7 +3639,12 @@ template void FlatGLTest::renderSkinning2D() { shader .setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.5f})) .draw(mesh); - } else if(flag == FlatGL2D::Flag::UniformBuffers) { + } else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.5f})) @@ -3281,6 +3702,19 @@ template void FlatGLTest::renderSkinning3D() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3354,7 +3788,12 @@ template void FlatGLTest::renderSkinning3D() { shader .setTransformationProjectionMatrix(Matrix4::scaling(Vector3{0.5f})) .draw(mesh); - } else if(flag == FlatGL3D::Flag::UniformBuffers) { + } else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix(Matrix4::scaling(Vector3{0.5f})) @@ -3409,6 +3848,19 @@ template void FlatGLTest::renderInstanced2D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3642,7 +4094,12 @@ template void FlatGLTest::renderInstanced2D() { shader.draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -3732,6 +4189,19 @@ template void FlatGLTest::renderInstanced3D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3969,7 +4439,12 @@ template void FlatGLTest::renderInstanced3D() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == FlatGL2D::Flag::UniformBuffers) { + else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -4060,6 +4535,19 @@ template void FlatGLTest::renderInstancedSkinning2D() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4143,7 +4631,12 @@ template void FlatGLTest::renderInstancedSkinning2D() { .setPerInstanceJointCount(5) .setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.3f})) .draw(mesh); - } else if(flag == FlatGL2D::Flag::UniformBuffers) { + } else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.3f})) @@ -4188,6 +4681,19 @@ template void FlatGLTest::renderInstancedSkinning3D() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == FlatGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4271,7 +4777,12 @@ template void FlatGLTest::renderInstancedSkinning3D() { .setPerInstanceJointCount(5) .setTransformationProjectionMatrix(Matrix4::scaling(Vector3{0.3f})) .draw(mesh); - } else if(flag == FlatGL3D::Flag::UniformBuffers) { + } else if(flag == FlatGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == FlatGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix(Matrix4::scaling(Vector3{0.3f})) @@ -4327,6 +4838,18 @@ void FlatGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= FlatGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= FlatGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -4532,13 +5055,13 @@ void FlatGLTest::renderMulti2D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(1211); drawData[1*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) .setObjectId(5627); drawData[2*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; @@ -4552,8 +5075,8 @@ void FlatGLTest::renderMulti2D() { }) .clearColor(1, Vector4ui{27}); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(FlatMaterialUniform), sizeof(FlatMaterialUniform)); @@ -4676,6 +5199,18 @@ void FlatGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= FlatGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= FlatGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -4887,13 +5422,13 @@ void FlatGLTest::renderMulti3D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(1211); drawData[1*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) .setObjectId(5627); drawData[2*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; @@ -4907,8 +5442,8 @@ void FlatGLTest::renderMulti3D() { }) .clearColor(1, Vector4ui{27}); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(FlatMaterialUniform), sizeof(FlatMaterialUniform)); @@ -5030,6 +5565,18 @@ void FlatGLTest::renderMultiSkinning2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= FlatGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= FlatGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -5164,20 +5711,20 @@ void FlatGLTest::renderMultiSkinning2D() { /* Material / joint offsets are zero if we have single draw, as those are done with UBO offset bindings instead */ drawData[0*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) /* Overlaps with the first joint set with two matrices, unless the padding in the single-draw case prevents that */ - .setJointOffset(data.drawCount == 1 ? 0 : 2); + .setJointOffset(data.bindWithOffset ? 0 : 2); drawData[2*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 6); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 6); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(FlatMaterialUniform), sizeof(FlatMaterialUniform)); @@ -5186,7 +5733,7 @@ void FlatGLTest::renderMultiSkinning2D() { sizeof(TransformationProjectionUniform2D)); shader.bindJointBuffer(jointUniform, 0*data.uniformIncrement*sizeof(TransformationUniform2D), - data.jointCount*sizeof(TransformationUniform2D)); + 4*sizeof(TransformationUniform2D)); shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(FlatDrawUniform), sizeof(FlatDrawUniform)); @@ -5200,7 +5747,7 @@ void FlatGLTest::renderMultiSkinning2D() { sizeof(TransformationProjectionUniform2D)); shader.bindJointBuffer(jointUniform, 1*data.uniformIncrement*sizeof(TransformationUniform2D), - data.jointCount*sizeof(TransformationUniform2D)); + 4*sizeof(TransformationUniform2D)); shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(FlatDrawUniform), sizeof(FlatDrawUniform)); @@ -5214,7 +5761,7 @@ void FlatGLTest::renderMultiSkinning2D() { sizeof(TransformationProjectionUniform2D)); shader.bindJointBuffer(jointUniform, 2*data.uniformIncrement*sizeof(TransformationUniform2D), - data.jointCount*sizeof(TransformationUniform2D)); + 4*sizeof(TransformationUniform2D)); shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(FlatDrawUniform), sizeof(FlatDrawUniform)); @@ -5266,6 +5813,18 @@ void FlatGLTest::renderMultiSkinning3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= FlatGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= FlatGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -5400,20 +5959,20 @@ void FlatGLTest::renderMultiSkinning3D() { /* Material / joint offsets are zero if we have single draw, as those are done with UBO offset bindings instead */ drawData[0*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) /* Overlaps with the first joint set with two matrices, unless the padding in the single-draw case prevents that */ - .setJointOffset(data.drawCount == 1 ? 0 : 2); + .setJointOffset(data.bindWithOffset ? 0 : 2); drawData[2*data.uniformIncrement] = FlatDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 6); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 6); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(FlatMaterialUniform), sizeof(FlatMaterialUniform)); @@ -5422,7 +5981,7 @@ void FlatGLTest::renderMultiSkinning3D() { sizeof(TransformationProjectionUniform3D)); shader.bindJointBuffer(jointUniform, 0*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(FlatDrawUniform), sizeof(FlatDrawUniform)); @@ -5436,7 +5995,7 @@ void FlatGLTest::renderMultiSkinning3D() { sizeof(TransformationProjectionUniform3D)); shader.bindJointBuffer(jointUniform, 1*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(FlatDrawUniform), sizeof(FlatDrawUniform)); @@ -5450,7 +6009,7 @@ void FlatGLTest::renderMultiSkinning3D() { sizeof(TransformationProjectionUniform3D)); shader.bindJointBuffer(jointUniform, 2*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(FlatDrawUniform), sizeof(FlatDrawUniform)); diff --git a/src/Magnum/Shaders/Test/FlatGL_Test.cpp b/src/Magnum/Shaders/Test/FlatGL_Test.cpp index 3a5496239..f6c51f9be 100644 --- a/src/Magnum/Shaders/Test/FlatGL_Test.cpp +++ b/src/Magnum/Shaders/Test/FlatGL_Test.cpp @@ -64,13 +64,10 @@ const struct { "expected at most 4 secondary per-vertex joints, got 5"}, {"joint count but no per-vertex joint count", 10, 0, 0, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, - {"per-vertex joint count but no joint count", - 0, 2, 0, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, - {"secondary per-vertex joint count but no joint count", - 0, 0, 3, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, + "count has to be zero if per-vertex joint count is zero"}, + /* The rest depends on flags being set and is thus verified in constructor, + tested in FlatGLTest::constructInvalid() and + constructUniformBuffersInvalid() */ }; #endif @@ -171,12 +168,24 @@ void FlatGL_Test::debugFlagsSupersets() { } #ifndef MAGNUM_TARGET_GLES2 - /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ { std::ostringstream out; Debug{&out} << (FlatGL3D::Flag::MultiDraw|FlatGL3D::Flag::UniformBuffers); CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::MultiDraw\n"); } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (FlatGL2D::Flag::ShaderStorageBuffers|FlatGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (FlatGL3D::Flag::MultiDraw|FlatGL3D::Flag::ShaderStorageBuffers|FlatGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::MultiDraw|Shaders::FlatGL::Flag::ShaderStorageBuffers\n"); + } + #endif #endif } diff --git a/src/Magnum/Shaders/Test/LineGLTest.cpp b/src/Magnum/Shaders/Test/LineGLTest.cpp index 0ee4163d8..591a04d6f 100644 --- a/src/Magnum/Shaders/Test/LineGLTest.cpp +++ b/src/Magnum/Shaders/Test/LineGLTest.cpp @@ -163,7 +163,10 @@ const struct { {"multiple materials, draws", LineGL2D::Flag::UniformBuffers, {}, {}, 16, 41}, {"object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::ObjectId, {}, {}, 1, 1}, {"instanced object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::InstancedObjectId, {}, {}, 1, 1}, - {"multidraw with all the things", LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId|LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::InstancedObjectId, {}, {}, 16, 41} + {"multidraw with all the things", LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId|LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::InstancedObjectId, {}, {}, 16, 41}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with all the things", LineGL2D::Flag::ShaderStorageBuffers|LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId|LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::InstancedObjectId, {}, {}, 0, 0}, + #endif }; const struct { @@ -172,6 +175,7 @@ const struct { UnsignedInt materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData[]{ + /* These two fail for UBOs but not SSBOs */ {"zero draws", LineGL2D::Flag::UniformBuffers, 1, 0, "draw count can't be zero"}, {"zero materials", LineGL2D::Flag::UniformBuffers, 0, 1, @@ -333,20 +337,51 @@ const struct { UnsignedInt expectedId[3]; LineGL2D::Flags flags; UnsignedInt materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; } RenderMultiData[]{ {"bind with offset", - {}, {}, 1, 1, 16}, + {}, + {}, + 1, 1, true, 16}, {"bind with offset, object ID", - {1211, 5627, 36363}, LineGL2D::Flag::ObjectId, 1, 1, 16}, + {1211, 5627, 36363}, + LineGL2D::Flag::ObjectId, + 1, 1, true, 16}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, object ID, shader storage", + {1211, 5627, 36363}, + LineGL2D::Flag::ShaderStorageBuffers|LineGL2D::Flag::ObjectId, + 0, 0, true, 16}, + #endif {"draw offset", - {}, {}, 2, 3, 1}, + {}, + {}, + 2, 3, false, 1}, {"draw offset, object ID", - {1211, 5627, 36363}, LineGL2D::Flag::ObjectId, 2, 3, 1}, + {1211, 5627, 36363}, + LineGL2D::Flag::ObjectId, + 2, 3, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, object ID, shader storage", + {1211, 5627, 36363}, + LineGL2D::Flag::ShaderStorageBuffers|LineGL2D::Flag::ObjectId, + 0, 0, false, 1}, + #endif {"multidraw", - {}, LineGL2D::Flag::MultiDraw, 2, 3, 1}, + {}, + LineGL2D::Flag::MultiDraw, + 2, 3, false, 1}, {"multidraw, object ID", - {1211, 5627, 36363}, LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId, 2, 3, 1}, + {1211, 5627, 36363}, + LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId, + 2, 3, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, object ID, shader storage", + {1211, 5627, 36363}, + LineGL2D::Flag::ShaderStorageBuffers|LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId, + 0, 0, false, 1}, + #endif }; LineGLTest::LineGLTest() { @@ -405,8 +440,15 @@ LineGLTest::LineGLTest() { addTests({ &LineGLTest::renderDefaults2D, &LineGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderDefaults2D, + #endif &LineGLTest::renderDefaults3D, - &LineGLTest::renderDefaults3D}, + &LineGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderDefaults3D + #endif + }, &LineGLTest::renderSetupSmall, &LineGLTest::renderTeardown); @@ -414,6 +456,9 @@ LineGLTest::LineGLTest() { addInstancedTests({ &LineGLTest::renderLineCapsJoins2D, &LineGLTest::renderLineCapsJoins2D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderLineCapsJoins2D, + #endif &LineGLTest::renderLineCapsJoins2DReversed, &LineGLTest::renderLineCapsJoins2DTransformed}, Containers::arraySize(RenderLineCapsJoins2DData), @@ -423,7 +468,11 @@ LineGLTest::LineGLTest() { /* MSVC needs explicit type due to default template args */ addInstancedTests({ &LineGLTest::renderCube3D, - &LineGLTest::renderCube3D}, + &LineGLTest::renderCube3D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderCube3D + #endif + }, Containers::arraySize(RenderCube3DData), &LineGLTest::renderSetupLarge, &LineGLTest::renderTeardown); @@ -436,12 +485,25 @@ LineGLTest::LineGLTest() { addTests({ &LineGLTest::renderVertexColor2D, &LineGLTest::renderVertexColor2D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderVertexColor2D, + #endif &LineGLTest::renderVertexColor2D, &LineGLTest::renderVertexColor2D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderVertexColor2D, + #endif &LineGLTest::renderVertexColor3D, &LineGLTest::renderVertexColor3D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderVertexColor3D, + #endif &LineGLTest::renderVertexColor3D, - &LineGLTest::renderVertexColor3D}, + &LineGLTest::renderVertexColor3D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderVertexColor3D, + #endif + }, &LineGLTest::renderSetupSmall, &LineGLTest::renderTeardown); @@ -449,8 +511,15 @@ LineGLTest::LineGLTest() { addInstancedTests({ &LineGLTest::renderObjectId2D, &LineGLTest::renderObjectId2D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderObjectId2D, + #endif &LineGLTest::renderObjectId3D, - &LineGLTest::renderObjectId3D}, + &LineGLTest::renderObjectId3D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderObjectId3D + #endif + }, Containers::arraySize(RenderObjectIdData), &LineGLTest::renderSetupSmall, &LineGLTest::renderTeardown); @@ -459,8 +528,15 @@ LineGLTest::LineGLTest() { addInstancedTests({ &LineGLTest::renderInstanced2D, &LineGLTest::renderInstanced2D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderInstanced2D, + #endif &LineGLTest::renderInstanced3D, - &LineGLTest::renderInstanced3D}, + &LineGLTest::renderInstanced3D, + #ifndef MAGNUM_TARGET_WEBGL + &LineGLTest::renderInstanced3D, + #endif + }, Containers::arraySize(RenderInstancedData), &LineGLTest::renderSetupSmall, &LineGLTest::renderTeardown); @@ -557,6 +633,18 @@ template void LineGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= LineGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= LineGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1080,6 +1168,19 @@ GL::Mesh generateLineMesh(std::initializer_list lineSegments) { } template void LineGLTest::renderDefaults2D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1120,7 +1221,12 @@ template void LineGLTest::renderDefaults2D() { if(flag == LineGL2D::Flag{}) { shader.draw(lines); - } else if(flag == LineGL2D::Flag::UniformBuffers) { + } else if(flag == LineGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -1153,6 +1259,19 @@ template void LineGLTest::renderDefaults2D() { } template void LineGLTest::renderDefaults3D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1196,7 +1315,12 @@ template void LineGLTest::renderDefaults3D() { if(flag == LineGL3D::Flag{}) { shader.draw(lines); - } else if(flag == LineGL3D::Flag::UniformBuffers) { + } else if(flag == LineGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -1282,6 +1406,19 @@ template void LineGLTest::renderLineCapsJoins2D() { auto&& data = RenderLineCapsJoins2DData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1322,7 +1459,12 @@ template void LineGLTest::renderLineCapsJoins2D() { if(data.miterAngleLimit) shader.setMiterAngleLimit(*data.miterAngleLimit); shader.draw(lines); - } else if(flag == LineGL2D::Flag::UniformBuffers) { + } else if(flag == LineGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -1498,6 +1640,19 @@ template void LineGLTest::renderCube3D() { auto&& data = RenderCube3DData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1628,7 +1783,12 @@ template void LineGLTest::renderCube3D() { if(data.miterLengthLimit) shader.setMiterLengthLimit(*data.miterLengthLimit); shader.draw(lines); - } else if(flag == LineGL3D::Flag::UniformBuffers) { + } else if(flag == LineGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix(projection*transformation) @@ -1713,6 +1873,19 @@ void LineGLTest::renderPerspective3D() { } template void LineGLTest::renderVertexColor2D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL2D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -1765,7 +1938,12 @@ template void LineGLTest::renderVertexColor2D() { .setWidth(4.0f) .setSmoothness(1.0f) .draw(lines); - } else if(flag == LineGL2D::Flag::UniformBuffers) { + } else if(flag == LineGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -1802,6 +1980,19 @@ template void LineGLTest::renderVertexColor2D() { } template void LineGLTest::renderVertexColor3D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -1857,7 +2048,12 @@ template void LineGLTest::renderVertexColor3D() { .setWidth(4.0f) .setSmoothness(1.0f) .draw(lines); - } else if(flag == LineGL3D::Flag::UniformBuffers) { + } else if(flag == LineGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -1897,6 +2093,19 @@ template void LineGLTest::renderObjectId2D() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1936,7 +2145,12 @@ template void LineGLTest::renderObjectId2D() { .setSmoothness(data.smoothness) .setObjectId(47365) .draw(lines); - } else if(flag == LineGL2D::Flag::UniformBuffers) { + } else if(flag == LineGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -1983,6 +2197,19 @@ template void LineGLTest::renderObjectId3D() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2024,7 +2251,12 @@ template void LineGLTest::renderObjectId3D() { .setSmoothness(data.smoothness) .setObjectId(47365) .draw(lines); - } else if(flag == LineGL3D::Flag::UniformBuffers) { + } else if(flag == LineGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -2071,6 +2303,19 @@ template void LineGLTest::renderInstanced2D() { auto&& data = RenderInstancedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2137,8 +2382,12 @@ template void LineGLTest::renderInstanced2D() { /* Gets added to the per-instance ID, if that's enabled as well */ shader.setObjectId(1000); shader.draw(lines); - - } else if(flag == LineGL2D::Flag::UniformBuffers) { + } else if(flag == LineGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -2206,6 +2455,19 @@ template void LineGLTest::renderInstanced3D() { auto&& data = RenderInstancedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == LineGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == LineGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2272,8 +2534,12 @@ template void LineGLTest::renderInstanced3D() { /* Gets added to the per-instance ID, if that's enabled as well */ shader.setObjectId(1000); shader.draw(lines); - - } else if(flag == LineGL2D::Flag::UniformBuffers) { + } else if(flag == LineGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == LineGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -2348,6 +2614,18 @@ void LineGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= LineGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= LineGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -2448,13 +2726,13 @@ void LineGLTest::renderMulti2D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = LineDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(1211); drawData[1*data.uniformIncrement] = LineDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) .setObjectId(5627); drawData[2*data.uniformIncrement] = LineDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; @@ -2464,8 +2742,8 @@ void LineGLTest::renderMulti2D() { GL::Renderer::BlendFunction::One, GL::Renderer::BlendFunction::OneMinusSourceAlpha); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(LineMaterialUniform), sizeof(LineMaterialUniform)); @@ -2564,6 +2842,18 @@ void LineGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= LineGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= LineGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -2663,13 +2953,13 @@ void LineGLTest::renderMulti3D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = LineDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(1211); drawData[1*data.uniformIncrement] = LineDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) .setObjectId(5627); drawData[2*data.uniformIncrement] = LineDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; @@ -2679,8 +2969,8 @@ void LineGLTest::renderMulti3D() { GL::Renderer::BlendFunction::One, GL::Renderer::BlendFunction::OneMinusSourceAlpha); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(LineMaterialUniform), sizeof(LineMaterialUniform)); diff --git a/src/Magnum/Shaders/Test/LineGL_Test.cpp b/src/Magnum/Shaders/Test/LineGL_Test.cpp index a60bb8327..63ee58a09 100644 --- a/src/Magnum/Shaders/Test/LineGL_Test.cpp +++ b/src/Magnum/Shaders/Test/LineGL_Test.cpp @@ -143,13 +143,26 @@ void LineGL_Test::debugFlagsSupersets() { std::ostringstream out; Debug{&out} << (LineGL3D::Flag::ObjectId|LineGL3D::Flag::InstancedObjectId); CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::InstancedObjectId\n"); + } - /* MultiDraw is a superset of UniformBuffers so only one should be printed */ - } { + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ + { std::ostringstream out; Debug{&out} << (LineGL3D::Flag::MultiDraw|LineGL3D::Flag::UniformBuffers); CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::MultiDraw\n"); } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (LineGL2D::Flag::ShaderStorageBuffers|LineGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ShaderStorageBuffers|LineGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::MultiDraw|Shaders::LineGL::Flag::ShaderStorageBuffers\n"); + } + #endif } }}}} diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index b47b4f2a0..02ed2077a 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -377,6 +377,12 @@ constexpr struct { 8, 55, 16, 4, 0}, {"multidraw with wireframe w/o GS and dynamic primary+secondary skinning per-vertex sets", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader|MeshVisualizerGL2D::Flag::DynamicPerVertexJointCount, 8, 55, 16, 3, 4}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with wireframe w/o GS, instancing and dynamic primary skinning per-vertex sets", MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader|MeshVisualizerGL2D::Flag::InstancedTransformation|MeshVisualizerGL2D::Flag::DynamicPerVertexJointCount, + 0, 0, 0, 4, 0}, + {"shader storage + multidraw with wireframe w/o GS and dynamic primary+secondary skinning per-vertex sets", MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader|MeshVisualizerGL2D::Flag::DynamicPerVertexJointCount, + 0, 0, 0, 3, 4}, + #endif /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -439,6 +445,12 @@ constexpr struct { 8, 55, 16, 4, 0}, {"multidraw with wireframe w/o GS and dynamic primary+secondary skinning per-vertex sets", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader|MeshVisualizerGL3D::Flag::DynamicPerVertexJointCount, 8, 55, 16, 3, 4}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with wireframe, instancing and dynamic primary skinning per-vertex sets", MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::InstancedTransformation|MeshVisualizerGL3D::Flag::DynamicPerVertexJointCount, + 0, 0, 0, 4, 0}, + {"shader storage + multidraw with wireframe and dynamic primary+secondary skinning per-vertex sets", MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::DynamicPerVertexJointCount, + 0, 0, 0, 3, 4}, + #endif /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -618,25 +630,53 @@ constexpr struct { constexpr struct { const char* name; MeshVisualizerGL2D::Flags flags; - UnsignedInt materialCount, drawCount; + UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount, materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData2D[] { - {"zero draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 0, + /* These two fail for UBOs but not SSBOs */ + {"zero draws", + MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 0, 0, 0, 1, 0, "draw count can't be zero"}, - {"zero materials", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 0, 1, - "material count can't be zero"} + {"zero materials", + MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 0, 0, 0, 0, 1, + "material count can't be zero"}, + /* These two fail for UBOs but not SSBOs */ + {"per-vertex joint count but no joint count", + MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 0, 2, 0, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, + {"secondary per-vertex joint count but no joint count", + MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 0, 0, 3, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, }; constexpr struct { const char* name; MeshVisualizerGL3D::Flags flags; - UnsignedInt materialCount, drawCount; + UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount, materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData3D[] { - {"zero draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 0, + /* These two fail for UBOs but not SSBOs */ + {"zero draws", + MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 0, 0, 0, 1, 0, "draw count can't be zero"}, - {"zero materials", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 0, 1, + {"zero materials", + MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 0, 0, 0, 0, 1, "material count can't be zero"}, + /* These two fail for UBOs but not SSBOs */ + {"per-vertex joint count but no joint count", + MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 0, 2, 0, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, + {"secondary per-vertex joint count but no joint count", + MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 0, 0, 3, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, }; #endif @@ -1093,96 +1133,115 @@ constexpr struct { const char* expected; MeshVisualizerGL2D::Flags flags; UnsignedInt materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData2D[] { #ifndef MAGNUM_TARGET_WEBGL {"bind with offset, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::Wireframe, - 1, 1, 16, + 1, 1, true, 16, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, #endif {"bind with offset, wireframe w/o GS", "multidraw-wireframe-nogeo2D.tga", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, - 1, 1, 16, + 1, 1, true, 16, /* Minor differences on ARM Mali */ 0.67f, 0.02f}, {"bind with offset, vertex ID", "multidraw-vertexid2D.tga", MeshVisualizerGL2D::Flag::VertexId, - 1, 1, 16, + 1, 1, true, 16, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, {"bind with offset, instanced object ID", "multidraw-instancedobjectid2D.tga", MeshVisualizerGL2D::Flag::InstancedObjectId, - 1, 1, 16, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, textured object ID", "multidraw-objectidtexture2D.tga", MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture, - 1, 1, 16, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, textured array object ID", "multidraw-objectidtexture2D.tga", MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, - 1, 1, 16, + 1, 1, true, 16, + 0.0f, 0.0f}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, textured array object ID, shader storage", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + 0, 0, true, 16, 0.0f, 0.0f}, + #endif #ifndef MAGNUM_TARGET_WEBGL {"draw offset, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::Wireframe, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, #endif {"draw offset, wireframe w/o GS", "multidraw-wireframe-nogeo2D.tga", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 0.67f, 0.02f}, {"draw offset, vertex ID", "multidraw-vertexid2D.tga", MeshVisualizerGL2D::Flag::VertexId, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, {"draw offset, instanced object ID", "multidraw-instancedobjectid2D.tga", MeshVisualizerGL2D::Flag::InstancedObjectId, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, textured object ID", "multidraw-objectidtexture2D.tga", MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, textured array object ID", "multidraw-objectidtexture2D.tga", MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, textured array object ID, shader storage", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + 0, 0, false, 1, + 0.0f, 0.0f}, + #endif + #ifndef MAGNUM_TARGET_WEBGL {"multidraw, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe, - 2, 3, 1, + 2, 3, false, 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, + 2, 3, false, 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, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, {"multidraw, instanced object ID", "multidraw-instancedobjectid2D.tga", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::InstancedObjectId, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, textured object ID", "multidraw-objectidtexture2D.tga", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, textured array object ID", "multidraw-objectidtexture2D.tga", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, - 2, 3, 1, + 2, 3, false, 1, + 0.0f, 0.0f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, textured array object ID, shader storage", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + 0, 0, false, 1, 0.0f, 0.0f}, + #endif }; constexpr struct { @@ -1190,99 +1249,124 @@ constexpr struct { const char* expected; MeshVisualizerGL3D::Flags flags; UnsignedInt materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData3D[] { #ifndef MAGNUM_TARGET_WEBGL {"bind with offset, wireframe", "multidraw-wireframe3D.tga", MeshVisualizerGL3D::Flag::Wireframe, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, true, 16, + 0.0f, 0.0f}, {"bind with offset, wireframe + TBN", "multidraw-wireframe-tbn3D.tga", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, true, 16, + 0.0f, 0.0f}, #endif {"bind with offset, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, - 1, 1, 16, + 1, 1, true, 16, /* Minor differences on ARM Mali */ 6.0f, 0.04f}, {"bind with offset, vertex ID", "multidraw-vertexid3D.tga", MeshVisualizerGL3D::Flag::VertexId, - 1, 1, 16, + 1, 1, true, 16, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, {"bind with offset, instanced object ID", "multidraw-instancedobjectid3D.tga", MeshVisualizerGL3D::Flag::InstancedObjectId, - 1, 1, 16, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, textured object ID", "multidraw-objectidtexture3D.tga", MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture, - 1, 1, 16, + 1, 1, true, 16, 0.0f, 0.0f}, {"bind with offset, textured array object ID", "multidraw-objectidtexture3D.tga", MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, - 1, 1, 16, + 1, 1, true, 16, 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, textured array object ID, shader storage", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + 0, 0, true, 16, + 0.0f, 0.0f}, + #endif + #ifndef MAGNUM_TARGET_WEBGL {"draw offset, wireframe", "multidraw-wireframe3D.tga", MeshVisualizerGL3D::Flag::Wireframe, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, + 0.0f, 0.0f}, {"draw offset, wireframe + TBN", "multidraw-wireframe-tbn3D.tga", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 1, + 0.0f, 0.0f}, #endif {"draw offset, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 6.0f, 0.04f}, {"draw offset, vertex ID", "multidraw-vertexid3D.tga", MeshVisualizerGL3D::Flag::VertexId, - 2, 3, 1, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, {"draw offset, instanced object ID", "multidraw-instancedobjectid3D.tga", MeshVisualizerGL3D::Flag::InstancedObjectId, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, textured object ID", "multidraw-objectidtexture3D.tga", MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"draw offset, textured array object ID", "multidraw-objectidtexture3D.tga", MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, textured array object ID, shader storage", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + 0, 0, false, 1, + 0.0f, 0.0f}, + #endif + #ifndef MAGNUM_TARGET_WEBGL {"multidraw, wireframe", "multidraw-wireframe3D.tga", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, false, 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}, + 2, 3, false, 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, + 2, 3, false, 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, + 2, 3, false, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, {"multidraw, instanced object ID", "multidraw-instancedobjectid3D.tga", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::InstancedObjectId, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, textured object ID", "multidraw-objectidtexture3D.tga", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture, - 2, 3, 1, + 2, 3, false, 1, 0.0f, 0.0f}, {"multidraw, textured array object ID", "multidraw-objectidtexture3D.tga", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, - 2, 3, 1, + 2, 3, false, 1, + 0.0f, 0.0f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, textured array object ID, shader storage", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + 0, 0, false, 1, 0.0f, 0.0f}, + #endif }; /* Same as in FlatGL and PhongGL tests */ @@ -1291,15 +1375,39 @@ const struct { MeshVisualizerGL2D::Flags flags2D; MeshVisualizerGL3D::Flags flags3D; UnsignedInt materialCount, drawCount, jointCount; + bool bindWithOffset; UnsignedInt uniformIncrement; } RenderMultiSkinningData[]{ {"bind with offset", - {}, {}, 1, 1, 4, 16}, + {}, + {}, + 1, 1, 4, true, 16}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, shader storage", + MeshVisualizerGL2D::Flag::ShaderStorageBuffers, + MeshVisualizerGL3D::Flag::ShaderStorageBuffers, + 0, 0, 0, true, 16}, + #endif {"draw offset", - {}, {}, 2, 3, 9, 1}, + {}, + {}, + 2, 3, 9, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, shader storage", + MeshVisualizerGL2D::Flag::ShaderStorageBuffers, + MeshVisualizerGL3D::Flag::ShaderStorageBuffers, + 0, 0, 0, false, 1}, + #endif {"multidraw", MeshVisualizerGL2D::Flag::MultiDraw, - MeshVisualizerGL3D::Flag::MultiDraw, 2, 3, 9, 1} + MeshVisualizerGL3D::Flag::MultiDraw, + 2, 3, 9, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, shader storage", + MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::MultiDraw, + MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::MultiDraw, + 0, 0, 0, false, 1} + #endif }; #endif @@ -1400,8 +1508,11 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addTests({ &MeshVisualizerGLTest::renderDefaultsWireframe2D, &MeshVisualizerGLTest::renderDefaultsWireframe2D, + &MeshVisualizerGLTest::renderDefaultsWireframe2D, &MeshVisualizerGLTest::renderDefaultsWireframe3D, - &MeshVisualizerGLTest::renderDefaultsWireframe3D}, + &MeshVisualizerGLTest::renderDefaultsWireframe3D, + &MeshVisualizerGLTest::renderDefaultsWireframe3D, + }, &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif @@ -1411,8 +1522,15 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addTests({ &MeshVisualizerGLTest::renderDefaultsObjectId2D, &MeshVisualizerGLTest::renderDefaultsObjectId2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsObjectId2D, + #endif &MeshVisualizerGLTest::renderDefaultsObjectId3D, - &MeshVisualizerGLTest::renderDefaultsObjectId3D}, + &MeshVisualizerGLTest::renderDefaultsObjectId3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsObjectId3D, + #endif + }, &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif @@ -1422,8 +1540,15 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addInstancedTests({ &MeshVisualizerGLTest::renderDefaultsInstancedObjectId2D, &MeshVisualizerGLTest::renderDefaultsInstancedObjectId2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsInstancedObjectId2D, + #endif &MeshVisualizerGLTest::renderDefaultsInstancedObjectId3D, - &MeshVisualizerGLTest::renderDefaultsInstancedObjectId3D}, + &MeshVisualizerGLTest::renderDefaultsInstancedObjectId3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsInstancedObjectId3D, + #endif + }, Containers::arraySize(RenderInstancedObjectIdDefaultsData), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); @@ -1434,15 +1559,28 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addTests({ &MeshVisualizerGLTest::renderDefaultsVertexId2D, &MeshVisualizerGLTest::renderDefaultsVertexId2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsVertexId2D, + #endif &MeshVisualizerGLTest::renderDefaultsVertexId3D, &MeshVisualizerGLTest::renderDefaultsVertexId3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsVertexId3D, + #endif &MeshVisualizerGLTest::renderDefaultsPrimitiveId2D, &MeshVisualizerGLTest::renderDefaultsPrimitiveId2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsPrimitiveId2D, + #endif &MeshVisualizerGLTest::renderDefaultsPrimitiveId3D, &MeshVisualizerGLTest::renderDefaultsPrimitiveId3D, #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsPrimitiveId3D, + #endif + #ifndef MAGNUM_TARGET_WEBGL &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal, &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal, + &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal, #endif }, &MeshVisualizerGLTest::renderSetup, @@ -1454,6 +1592,9 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderWireframe2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderWireframe2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderWireframe2D, + #endif #endif }, Containers::arraySize(RenderWireframeData2D), @@ -1465,6 +1606,9 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderWireframe3D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderWireframe3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderWireframe3D, + #endif #endif }, Containers::arraySize(RenderWireframeData3D), @@ -1476,8 +1620,14 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addInstancedTests({ &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, + #endif &MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D, &MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D, + #endif }, Containers::arraySize(RenderObjectVertexPrimitiveIdData), &MeshVisualizerGLTest::renderSetup, @@ -1493,6 +1643,7 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addInstancedTests({ &MeshVisualizerGLTest::renderTangentBitangentNormal, &MeshVisualizerGLTest::renderTangentBitangentNormal, + &MeshVisualizerGLTest::renderTangentBitangentNormal, }, Containers::arraySize(RenderTangentBitangentNormalData), &MeshVisualizerGLTest::renderSetup, @@ -1504,8 +1655,15 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addInstancedTests({ &MeshVisualizerGLTest::renderSkinningWireframe2D, &MeshVisualizerGLTest::renderSkinningWireframe2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderSkinningWireframe2D, + #endif &MeshVisualizerGLTest::renderSkinningWireframe3D, - &MeshVisualizerGLTest::renderSkinningWireframe3D}, + &MeshVisualizerGLTest::renderSkinningWireframe3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderSkinningWireframe3D + #endif + }, Containers::arraySize(RenderSkinningData), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); @@ -1516,6 +1674,9 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderInstanced2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderInstanced2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderInstanced2D, + #endif #endif }, Containers::arraySize(RenderInstancedData2D), @@ -1525,6 +1686,9 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderInstanced3D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderInstanced3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderInstanced3D, + #endif #endif }, Containers::arraySize(RenderInstancedData3D), @@ -1536,8 +1700,15 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addTests({ &MeshVisualizerGLTest::renderInstancedSkinningWireframe2D, &MeshVisualizerGLTest::renderInstancedSkinningWireframe2D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderInstancedSkinningWireframe2D, + #endif &MeshVisualizerGLTest::renderInstancedSkinningWireframe3D, - &MeshVisualizerGLTest::renderInstancedSkinningWireframe3D}, + &MeshVisualizerGLTest::renderInstancedSkinningWireframe3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderInstancedSkinningWireframe3D + #endif + }, &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif @@ -1879,6 +2050,18 @@ void MeshVisualizerGLTest::constructUniformBuffers2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1965,6 +2148,18 @@ void MeshVisualizerGLTest::constructUniformBuffers3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -2259,6 +2454,7 @@ void MeshVisualizerGLTest::constructUniformBuffersInvalid2D() { Error redirectError{&out}; MeshVisualizerGL2D{MeshVisualizerGL2D::Configuration{} .setFlags(data.flags) + .setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount) .setMaterialCount(data.materialCount) .setDrawCount(data.drawCount)}; CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL2D: {}\n", data.message)); @@ -2279,6 +2475,7 @@ void MeshVisualizerGLTest::constructUniformBuffersInvalid3D() { Error redirectError{&out}; MeshVisualizerGL3D{MeshVisualizerGL3D::Configuration{} .setFlags(data.flags) + .setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount) .setMaterialCount(data.materialCount) .setDrawCount(data.drawCount)}; CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL3D: {}\n", data.message)); @@ -2980,7 +3177,17 @@ void MeshVisualizerGLTest::renderTeardown() { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) template void MeshVisualizerGLTest::renderDefaultsWireframe2D() { - if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); #ifndef MAGNUM_TARGET_GLES @@ -3009,7 +3216,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL2D::Flag{}) { shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -3046,7 +3258,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL2D::Flag{}) { shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -3074,7 +3291,17 @@ template void MeshVisualizerGLTest::renderDefault } template void MeshVisualizerGLTest::renderDefaultsWireframe3D() { - if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); #ifndef MAGNUM_TARGET_GLES @@ -3103,7 +3330,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(sphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3144,7 +3376,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(sphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3178,6 +3415,19 @@ template void MeshVisualizerGLTest::renderDefault #ifndef MAGNUM_TARGET_GLES2 template void MeshVisualizerGLTest::renderDefaultsObjectId2D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3200,7 +3450,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL2D::Flag{}) { shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -3232,6 +3487,19 @@ template void MeshVisualizerGLTest::renderDefault } template void MeshVisualizerGLTest::renderDefaultsObjectId3D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3254,7 +3522,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(icosphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3294,6 +3567,19 @@ template void MeshVisualizerGLTest::renderDefault auto&& data = RenderInstancedObjectIdDefaultsData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3336,7 +3622,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL2D::Flag{}) { shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -3371,6 +3662,19 @@ template void MeshVisualizerGLTest::renderDefault auto&& data = RenderInstancedObjectIdDefaultsData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3413,7 +3717,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(icosphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3449,6 +3758,19 @@ template void MeshVisualizerGLTest::renderDefault } template void MeshVisualizerGLTest::renderDefaultsVertexId2D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3474,7 +3796,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL2D::Flag{}) { shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -3501,6 +3828,19 @@ template void MeshVisualizerGLTest::renderDefault } template void MeshVisualizerGLTest::renderDefaultsVertexId3D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3526,7 +3866,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(icosphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3557,6 +3902,19 @@ template void MeshVisualizerGLTest::renderDefault } template void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3611,7 +3969,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL2D::Flag{}) { shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -3639,6 +4002,19 @@ template void MeshVisualizerGLTest::renderDefault } template void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3693,7 +4069,12 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(icosphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3727,7 +4108,17 @@ template void MeshVisualizerGLTest::renderDefault #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) template void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() { - if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); #ifndef MAGNUM_TARGET_GLES @@ -3754,7 +4145,8 @@ template void MeshVisualizerGLTest::renderDefault if(flag == MeshVisualizerGL3D::Flag{}) { shader.draw(sphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -3797,6 +4189,19 @@ template void MeshVisualizerGLTest::renderWirefra setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3860,7 +4265,12 @@ template void MeshVisualizerGLTest::renderWirefra .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -3930,6 +4340,19 @@ template void MeshVisualizerGLTest::renderWirefra setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3994,7 +4417,12 @@ template void MeshVisualizerGLTest::renderWirefra .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) @@ -4079,6 +4507,19 @@ template void MeshVisualizerGLTest::renderObjectV auto&& data = RenderObjectVertexPrimitiveIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4153,7 +4594,7 @@ template void MeshVisualizerGLTest::renderObjectV GL::Mesh circle = MeshTools::compile(circleData); MeshVisualizerGL2D::Flags flags = data.flags2D|flag; - if(flag == MeshVisualizerGL2D::Flag::UniformBuffers && (data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) && !(data.flags2D & MeshVisualizerGL2D::Flag::TextureTransformation)) { + if(flag & MeshVisualizerGL2D::Flag::UniformBuffers && (data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) && !(data.flags2D & MeshVisualizerGL2D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= MeshVisualizerGL2D::Flag::TextureTransformation; } @@ -4179,7 +4620,7 @@ template void MeshVisualizerGLTest::renderObjectV .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindObjectIdTexture(textureArray); - if(flag != MeshVisualizerGL2D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & MeshVisualizerGL2D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else { texture = GL::Texture2D{}; @@ -4218,7 +4659,12 @@ template void MeshVisualizerGLTest::renderObjectV if(data.textureTransformation != Matrix3{}) shader.setTextureMatrix(data.textureTransformation); shader.draw(circle); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ /* See above for comments */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} @@ -4272,6 +4718,19 @@ template void MeshVisualizerGLTest::renderObjectV auto&& data = RenderObjectVertexPrimitiveIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4341,7 +4800,7 @@ template void MeshVisualizerGLTest::renderObjectV GL::Mesh sphere = MeshTools::compile(sphereData); MeshVisualizerGL3D::Flags flags = data.flags3D|flag; - if(flag == MeshVisualizerGL3D::Flag::UniformBuffers && (data.flags3D & MeshVisualizerGL3D::Flag::TextureArrays) && !(data.flags3D & MeshVisualizerGL3D::Flag::TextureTransformation)) { + if(flag & MeshVisualizerGL3D::Flag::UniformBuffers && (data.flags3D & MeshVisualizerGL3D::Flag::TextureArrays) && !(data.flags3D & MeshVisualizerGL3D::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= MeshVisualizerGL3D::Flag::TextureTransformation; } @@ -4367,7 +4826,7 @@ template void MeshVisualizerGLTest::renderObjectV .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindObjectIdTexture(textureArray); - if(flag != MeshVisualizerGL3D::Flag::UniformBuffers && data.layer != 0) + if(!(flag & MeshVisualizerGL3D::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else { texture = GL::Texture2D{}; @@ -4409,7 +4868,12 @@ template void MeshVisualizerGLTest::renderObjectV if(data.textureTransformation != Matrix3{}) shader.setTextureMatrix(data.textureTransformation); shader.draw(sphere); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ /* See above for comments */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} @@ -4526,7 +4990,17 @@ template void MeshVisualizerGLTest::renderTangent auto&& data = RenderTangentBitangentNormalData[testCaseInstanceId()]; setTestCaseDescription(data.name); - if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); #ifndef MAGNUM_TARGET_GLES @@ -4659,7 +5133,8 @@ template void MeshVisualizerGLTest::renderTangent else if(data.flags & MeshVisualizerGL3D::Flag::ObjectId) shader.setObjectId(127); shader.draw(mesh); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) @@ -4729,6 +5204,19 @@ template void MeshVisualizerGLTest::renderSkinnin CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4807,7 +5295,12 @@ template void MeshVisualizerGLTest::renderSkinnin .setColor(0xffff99_rgbf) .setWireframeColor(0x9999ff_rgbf) .draw(mesh); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.5f})) @@ -4867,6 +5360,19 @@ template void MeshVisualizerGLTest::renderSkinnin CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4945,7 +5451,12 @@ template void MeshVisualizerGLTest::renderSkinnin .setColor(0xffff99_rgbf) .setWireframeColor(0x9999ff_rgbf) .draw(mesh); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -5006,6 +5517,19 @@ template void MeshVisualizerGLTest::renderInstanc setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -5213,7 +5737,12 @@ template void MeshVisualizerGLTest::renderInstanc shader.draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -5281,6 +5810,19 @@ template void MeshVisualizerGLTest::renderInstanc setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -5508,7 +6050,12 @@ template void MeshVisualizerGLTest::renderInstanc shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -5588,6 +6135,19 @@ template void MeshVisualizerGLTest::renderInstanc CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -5675,7 +6235,12 @@ template void MeshVisualizerGLTest::renderInstanc .setColor(0xffff99_rgbf) .setWireframeColor(0x9999ff_rgbf) .draw(mesh); - } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.3f})) @@ -5723,6 +6288,19 @@ template void MeshVisualizerGLTest::renderInstanc CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -5810,7 +6388,12 @@ template void MeshVisualizerGLTest::renderInstanc .setColor(0xffff99_rgbf) .setWireframeColor(0x9999ff_rgbf) .draw(mesh); - } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == MeshVisualizerGL3D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -5878,6 +6461,18 @@ void MeshVisualizerGLTest::renderMulti2D() { } #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -6077,18 +6672,18 @@ void MeshVisualizerGLTest::renderMulti2D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) .setObjectId(0); drawData[1*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(4); drawData[2*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(8); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 0*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), sizeof(MeshVisualizerMaterialUniform)); @@ -6201,6 +6796,18 @@ void MeshVisualizerGLTest::renderMulti3D() { } #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -6411,18 +7018,18 @@ void MeshVisualizerGLTest::renderMulti3D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. Also no need to supply a normal matrix. */ drawData[0*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) .setObjectId(0); drawData[1*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(10); drawData[2*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) .setObjectId(20); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 0*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), sizeof(MeshVisualizerMaterialUniform)); @@ -6528,6 +7135,18 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags2D >= MeshVisualizerGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags2D >= MeshVisualizerGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -6660,20 +7279,20 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe2D() { /* Material / joint offsets are zero if we have single draw, as those are done with UBO offset bindings instead */ drawData[0*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) /* Overlaps with the first joint set with two matrices, unless the padding in the single-draw case prevents that */ - .setJointOffset(data.drawCount == 1 ? 0 : 2); + .setJointOffset(data.bindWithOffset ? 0 : 2); drawData[2*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 6); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 6); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), sizeof(MeshVisualizerMaterialUniform)); @@ -6682,7 +7301,7 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe2D() { sizeof(TransformationProjectionUniform2D)); shader.bindJointBuffer(jointUniform, 0*data.uniformIncrement*sizeof(TransformationUniform2D), - data.jointCount*sizeof(TransformationUniform2D)); + 4*sizeof(TransformationUniform2D)); shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), sizeof(MeshVisualizerDrawUniform2D)); @@ -6696,7 +7315,7 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe2D() { sizeof(TransformationProjectionUniform2D)); shader.bindJointBuffer(jointUniform, 1*data.uniformIncrement*sizeof(TransformationUniform2D), - data.jointCount*sizeof(TransformationUniform2D)); + 4*sizeof(TransformationUniform2D)); shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), sizeof(MeshVisualizerDrawUniform2D)); @@ -6710,7 +7329,7 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe2D() { sizeof(TransformationProjectionUniform2D)); shader.bindJointBuffer(jointUniform, 2*data.uniformIncrement*sizeof(TransformationUniform2D), - data.jointCount*sizeof(TransformationUniform2D)); + 4*sizeof(TransformationUniform2D)); shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), sizeof(MeshVisualizerDrawUniform2D)); @@ -6762,6 +7381,18 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags3D >= MeshVisualizerGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags3D >= MeshVisualizerGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -6894,22 +7525,22 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe3D() { /* Material / joint offsets are zero if we have single draw, as those are done with UBO offset bindings instead */ drawData[0*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) /* Overlaps with the first joint set with two matrices, unless the padding in the single-draw case prevents that */ - .setJointOffset(data.drawCount == 1 ? 0 : 2); + .setJointOffset(data.bindWithOffset ? 0 : 2); drawData[2*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 6); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 6); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; shader.bindProjectionBuffer(projectionUniform); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), sizeof(MeshVisualizerMaterialUniform)); @@ -6918,7 +7549,7 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe3D() { sizeof(TransformationUniform3D)); shader.bindJointBuffer(jointUniform, 0*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), sizeof(MeshVisualizerDrawUniform3D)); @@ -6932,7 +7563,7 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe3D() { sizeof(TransformationUniform3D)); shader.bindJointBuffer(jointUniform, 1*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), sizeof(MeshVisualizerDrawUniform3D)); @@ -6946,7 +7577,7 @@ void MeshVisualizerGLTest::renderMultiSkinningWireframe3D() { sizeof(TransformationUniform3D)); shader.bindJointBuffer(jointUniform, 2*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), sizeof(MeshVisualizerDrawUniform3D)); diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp index a8e48e3a7..84afcfe36 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp @@ -74,13 +74,10 @@ const struct { "expected at most 4 secondary per-vertex joints, got 5"}, {"joint count but no per-vertex joint count", 10, 0, 0, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, - {"per-vertex joint count but no joint count", - 0, 2, 0, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, - {"secondary per-vertex joint count but no joint count", - 0, 0, 3, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, + "count has to be zero if per-vertex joint count is zero"}, + /* The rest depends on flags being set and is thus verified in constructor, + tested in MeshVisualizerGLTest::constructInvalid() and + constructUniformBuffersInvalid() */ }; #endif @@ -243,12 +240,24 @@ void MeshVisualizerGL_Test::debugFlagsSupersets2D() { CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::InstancedTextureOffset\n"); } - /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ { std::ostringstream out; Debug{&out} << (MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::UniformBuffers); CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::MultiDraw\n"); } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::ShaderStorageBuffers|MeshVisualizerGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::MultiDraw|Shaders::MeshVisualizerGL2D::Flag::ShaderStorageBuffers\n"); + } + #endif } void MeshVisualizerGL_Test::debugFlagsSupersets3D() { @@ -276,12 +285,24 @@ void MeshVisualizerGL_Test::debugFlagsSupersets3D() { CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::InstancedTextureOffset\n"); } - /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ { std::ostringstream out; Debug{&out} << (MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::UniformBuffers); CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::MultiDraw\n"); } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::ShaderStorageBuffers|MeshVisualizerGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::MultiDraw|Shaders::MeshVisualizerGL3D::Flag::ShaderStorageBuffers\n"); + } + #endif } #endif diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index a350473d6..514455e83 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -363,15 +363,23 @@ constexpr struct { 8, 4, 16, 24, 16, 4, 0}, {"multidraw with all the things except instancing", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::LightCulling|PhongGL::Flag::DynamicPerVertexJointCount, 8, 4, 16, 24, 16, 3, 4}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with all the things except secondary per-vertex sets", PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::LightCulling|PhongGL::Flag::DynamicPerVertexJointCount, + 0, 4, 0, 0, 0, 4, 0}, + {"shader storage + multidraw with all the things except instancing", PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::LightCulling|PhongGL::Flag::DynamicPerVertexJointCount, + 0, 4, 0, 0, 0, 3, 4}, + #endif }; #endif constexpr struct { const char* name; PhongGL::Flags flags; - UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount; + UnsignedInt lightCount, perDrawLightCount, jointCount, perVertexJointCount, secondaryPerVertexJointCount; const char* message; } ConstructInvalidData[] { + {"per-draw light count larger than total count", {}, 10, 11, 0, 0, 0, + "per-draw light count expected to not be larger than total count of 10, got 11"}, {"texture transformation but not textured", /* ObjectId shares bits with ObjectIdTexture but should still trigger the assert */ @@ -380,32 +388,32 @@ constexpr struct { |PhongGL::Flag::ObjectId #endif , - 0, 0, 0, + 1, 1, 0, 0, 0, "texture transformation enabled but the shader is not textured"}, #ifndef MAGNUM_TARGET_GLES2 {"texture arrays but not textured", /* ObjectId shares bits with ObjectIdTexture but should still trigger the assert */ PhongGL::Flag::TextureArrays|PhongGL::Flag::ObjectId, - 0, 0, 0, + 1, 1, 0, 0, 0, "texture arrays enabled but the shader is not textured"}, {"conflicting bitangent and instanced object id attribute", PhongGL::Flag::Bitangent|PhongGL::Flag::InstancedObjectId, - 0, 0, 0, + 1, 1, 0, 0, 0, "Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead"}, #endif {"specular texture but no specular", PhongGL::Flag::SpecularTexture|PhongGL::Flag::NoSpecular, - 0, 0, 0, + 1, 1, 0, 0, 0, "specular texture requires the shader to not have specular disabled"}, #ifndef MAGNUM_TARGET_GLES2 {"dynamic per-vertex joint count but no static per-vertex joint count", PhongGL::Flag::DynamicPerVertexJointCount, - 0, 0, 0, + 1, 1, 0, 0, 0, "dynamic per-vertex joint count enabled for zero joints"}, {"instancing together with secondary per-vertex sets", PhongGL::Flag::InstancedTransformation, - 10, 4, 1, + 1, 1, 10, 4, 1, "TransformationMatrix attribute binding conflicts with the SecondaryJointIds / SecondaryWeights attributes, use a non-instanced rendering with secondary weights instead"} #endif }; @@ -414,17 +422,39 @@ constexpr struct { constexpr struct { const char* name; PhongGL::Flags flags; - UnsignedInt materialCount, drawCount; + UnsignedInt lightCount, perDrawLightCount, jointCount, perVertexJointCount, secondaryPerVertexJointCount, materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData[]{ - {"zero draws", PhongGL::Flag::UniformBuffers, 1, 0, + /* These three fail for UBOs but not SSBOs */ + {"per-draw light count larger than total count", + PhongGL::Flag::UniformBuffers, + 10, 11, 0, 0, 0, 1, 1, + "per-draw light count expected to not be larger than total count of 10, got 11"}, + {"zero draws", + PhongGL::Flag::UniformBuffers, + 1, 1, 0, 0, 0, 1, 0, "draw count can't be zero"}, - {"zero materials", PhongGL::Flag::UniformBuffers, 0, 1, + {"zero materials", + PhongGL::Flag::UniformBuffers, + 1, 1, 0, 0, 0, 0, 1, "material count can't be zero"}, - {"texture arrays but no transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, 1, 1, + {"texture arrays but no transformation", + PhongGL::Flag::UniformBuffers|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 1, 1, 0, 0, 0, 1, 1, "texture arrays require texture transformation enabled as well if uniform buffers are used"}, - {"light culling but no UBOs", PhongGL::Flag::LightCulling, 1, 1, - "light culling requires uniform buffers to be enabled"} + {"light culling but no UBOs", + PhongGL::Flag::LightCulling, + 1, 1, 0, 0, 0, 1, 1, + "light culling requires uniform buffers to be enabled"}, + /* These two fail for UBOs but not SSBOs */ + {"per-vertex joint count but no joint count", + PhongGL::Flag::UniformBuffers, + 1, 1, 0, 2, 0, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, + {"secondary per-vertex joint count but no joint count", + PhongGL::Flag::UniformBuffers, + 1, 1, 0, 0, 3, 1, 1, + "joint count can't be zero if per-vertex joint count is non-zero"}, }; #endif @@ -801,10 +831,14 @@ const struct { #ifndef MAGNUM_TARGET_GLES2 const struct { const char* name; + PhongGL::Flags flags; UnsignedInt count, perDrawCount; } RenderLightCullingData[]{ - {"same count and per-draw count", 64, 64}, - {"per-draw count lower", 64, 2} + {"same count and per-draw count", {}, 64, 64}, + {"per-draw count lower", {}, 64, 2}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage buffers, per-draw count only", PhongGL::Flag::ShaderStorageBuffers, 0, 2}, + #endif }; #endif @@ -990,132 +1024,160 @@ constexpr struct { UnsignedInt expectedId[3]; PhongGL::Flags flags; UnsignedInt lightCount, perDrawLightCount, materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset, colored", "multidraw.tga", {}, {}, - 2, 2, 1, 1, 16, + 2, 2, 1, 1, true, 16, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"bind with offset, colored + object ID", "multidraw.tga", {1211, 5627, 36363}, PhongGL::Flag::ObjectId, - 2, 2, 1, 1, 16, + 2, 2, 1, 1, true, 16, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"bind with offset, colored + textured object ID", "multidraw.tga", {3211, 8627, 40363}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture, - 2, 2, 1, 1, 16, + 2, 2, 1, 1, true, 16, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"bind with offset, colored + textured array object ID", "multidraw.tga", {3211, 8627, 40363}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, - 2, 2, 1, 1, 16, + 2, 2, 1, 1, true, 16, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"bind with offset, textured", "multidraw-textured.tga", {}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, - 2, 2, 1, 1, 16, + 2, 2, 1, 1, true, 16, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, {"bind with offset, texture array", "multidraw-textured.tga", {}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, - 2, 2, 1, 1, 16, + 2, 2, 1, 1, true, 16, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 50.34f, 0.131f}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, texture array, shader storage", + "multidraw-textured.tga", {}, + PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 0, 2, 0, 0, true, 16, /* Some difference at the UV edge (texture is wrapping in the 2D case while the 2D array has a black area around) */ 50.34f, 0.131f}, + #endif {"draw offset, colored", "multidraw.tga", {}, {}, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"draw offset, colore, less per-draw lights", "multidraw.tga", {}, {}, - 4, 2, 2, 3, 1, + 4, 2, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"draw offset, colored + object ID", "multidraw.tga", {1211, 5627, 36363}, PhongGL::Flag::ObjectId, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"draw offset, colored + textured object ID", "multidraw.tga", {3211, 8627, 40363}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"draw offset, colored + textured array object ID", "multidraw.tga", {3211, 8627, 40363}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"draw offset, textured", "multidraw-textured.tga", {}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, {"draw offset, texture array", "multidraw-textured.tga", {}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Some difference at the UV edge (texture is wrapping in the 2D case while the 2D array has a black area around) */ 50.34f, 0.131f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, texture array, shader storage", + "multidraw-textured.tga", {}, + PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 0, 2, 0, 0, false, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 50.34f, 0.131f}, + #endif {"multidraw, colored", "multidraw.tga", {}, PhongGL::Flag::MultiDraw, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"multidraw, colored, less per-draw lights", "multidraw.tga", {}, PhongGL::Flag::MultiDraw, - 4, 2, 2, 3, 1, + 4, 2, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"multidraw, colored + object ID", "multidraw.tga", {1211, 5627, 36363}, PhongGL::Flag::MultiDraw|PhongGL::Flag::ObjectId, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"multidraw, colored + textured object ID", "multidraw.tga", {3211, 8627, 40363}, PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, {"multidraw, colored + textured array object ID", "multidraw.tga", {3211, 8627, 40363}, PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 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, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, {"multidraw, texture array", "multidraw-textured.tga", {}, PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, - 4, 4, 2, 3, 1, + 4, 4, 2, 3, false, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 50.34f, 0.131f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, texture array, shader storage", + "multidraw-textured.tga", {}, + PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 0, 4, 0, 0, false, 1, /* Some difference at the UV edge (texture is wrapping in the 2D case while the 2D array has a black area around) */ 50.34f, 0.131f}, + #endif /** @todo test normal and per-draw scaling when there's usable texture */ }; @@ -1124,14 +1186,33 @@ const struct { const char* name; PhongGL::Flags flags; UnsignedInt materialCount, drawCount, jointCount; + bool bindWithOffset; UnsignedInt uniformIncrement; } RenderMultiSkinningData[]{ {"bind with offset", - {}, 1, 1, 4, 16}, + {}, + 1, 1, 4, true, 16}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, shader storage", + PhongGL::Flag::ShaderStorageBuffers, + 0, 0, 0, true, 16}, + #endif {"draw offset", - {}, 2, 3, 9, 1}, + {}, + 2, 3, 9, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, shader storage", + PhongGL::Flag::ShaderStorageBuffers, + 0, 0, 0, false, 1}, + #endif {"multidraw", - PhongGL::Flag::MultiDraw, 2, 3, 9, 1} + PhongGL::Flag::MultiDraw, + 2, 3, 9, false, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, shader storage", + PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::MultiDraw, + 0, 0, 0, false, 1}, + #endif }; #endif @@ -1210,7 +1291,10 @@ PhongGLTest::PhongGLTest() { addTests({ &PhongGLTest::renderDefaults, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderDefaults + &PhongGLTest::renderDefaults, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderDefaults, + #endif #endif }, &PhongGLTest::renderSetup, @@ -1220,7 +1304,10 @@ PhongGLTest::PhongGLTest() { addInstancedTests({ &PhongGLTest::renderColored, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderColored + &PhongGLTest::renderColored, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderColored, + #endif #endif }, Containers::arraySize(RenderColoredData), @@ -1231,7 +1318,10 @@ PhongGLTest::PhongGLTest() { addInstancedTests({ &PhongGLTest::renderSinglePixelTextured, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderSinglePixelTextured + &PhongGLTest::renderSinglePixelTextured, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderSinglePixelTextured, + #endif #endif }, Containers::arraySize(RenderSinglePixelTexturedData), @@ -1242,7 +1332,10 @@ PhongGLTest::PhongGLTest() { addInstancedTests({ &PhongGLTest::renderTextured, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderTextured + &PhongGLTest::renderTextured, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderTextured, + #endif #endif }, Containers::arraySize(RenderTexturedData), @@ -1253,7 +1346,10 @@ PhongGLTest::PhongGLTest() { addInstancedTests({ &PhongGLTest::renderTexturedNormal, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderTexturedNormal + &PhongGLTest::renderTexturedNormal, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderTexturedNormal, + #endif #endif }, Containers::arraySize(RenderTexturedNormalData), @@ -1265,10 +1361,16 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderVertexColor, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderVertexColor, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderVertexColor, + #endif #endif &PhongGLTest::renderVertexColor, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderVertexColor, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderVertexColor, + #endif #endif }, &PhongGLTest::renderSetup, @@ -1279,6 +1381,9 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderShininess, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderShininess, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderShininess, + #endif #endif }, Containers::arraySize(RenderShininessData), @@ -1289,7 +1394,10 @@ PhongGLTest::PhongGLTest() { addInstancedTests({ &PhongGLTest::renderAlpha, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderAlpha + &PhongGLTest::renderAlpha, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderAlpha, + #endif #endif }, Containers::arraySize(RenderAlphaData), @@ -1300,7 +1408,11 @@ PhongGLTest::PhongGLTest() { /* MSVC needs explicit type due to default template args */ addInstancedTests({ &PhongGLTest::renderObjectId, - &PhongGLTest::renderObjectId}, + &PhongGLTest::renderObjectId, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderObjectId + #endif + }, Containers::arraySize(RenderObjectIdData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); @@ -1311,6 +1423,9 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderLights, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderLights, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderLights, + #endif #endif }, Containers::arraySize(RenderLightsData), @@ -1333,7 +1448,10 @@ PhongGLTest::PhongGLTest() { addTests({ &PhongGLTest::renderZeroLights, #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::renderZeroLights + &PhongGLTest::renderZeroLights, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderZeroLights + #endif #endif }, &PhongGLTest::renderSetup, @@ -1343,7 +1461,11 @@ PhongGLTest::PhongGLTest() { /* MSVC needs explicit type due to default template args */ addInstancedTests({ &PhongGLTest::renderSkinning, - &PhongGLTest::renderSkinning}, + &PhongGLTest::renderSkinning, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderSkinning, + #endif + }, Containers::arraySize(RenderSkinningData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); @@ -1354,6 +1476,9 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderInstanced, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderInstanced, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderInstanced, + #endif #endif }, Containers::arraySize(RenderInstancedData), @@ -1364,7 +1489,11 @@ PhongGLTest::PhongGLTest() { /* MSVC needs explicit type due to default template args */ addTests({ &PhongGLTest::renderInstancedSkinning, - &PhongGLTest::renderInstancedSkinning}, + &PhongGLTest::renderInstancedSkinning, + #ifndef MAGNUM_TARGET_WEBGL + &PhongGLTest::renderInstancedSkinning, + #endif + }, &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); #endif @@ -1507,6 +1636,18 @@ void PhongGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= PhongGL::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1679,6 +1820,7 @@ void PhongGLTest::constructInvalid() { Error redirectError{&out}; PhongGL{PhongGL::Configuration{} .setFlags(data.flags) + .setLightCount(data.lightCount, data.perDrawLightCount) #ifndef MAGNUM_TARGET_GLES2 .setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount) #endif @@ -1703,6 +1845,8 @@ void PhongGLTest::constructUniformBuffersInvalid() { Error redirectError{&out}; PhongGL{PhongGL::Configuration{} .setFlags(data.flags) + .setLightCount(data.lightCount, data.perDrawLightCount) + .setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount) .setMaterialCount(data.materialCount) .setDrawCount(data.drawCount)}; CORRADE_COMPARE(out.str(), Utility::formatString( @@ -2126,6 +2270,19 @@ void PhongGLTest::renderTeardown() { template void PhongGLTest::renderDefaults() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2150,7 +2307,12 @@ template void PhongGLTest::renderDefaults() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -2203,6 +2365,19 @@ template void PhongGLTest::renderColored() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2254,7 +2429,12 @@ template void PhongGLTest::renderColored() { } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) @@ -2336,6 +2516,19 @@ template void PhongGLTest::renderSinglePixelTextured() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2361,7 +2554,7 @@ template void PhongGLTest::renderSinglePixelTextured() { PhongGL::Flags flags = PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + if(flag & PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= PhongGL::Flag::TextureTransformation; } @@ -2410,7 +2603,7 @@ template void PhongGLTest::renderSinglePixelTextured() { .bindAmbientTexture(ambientArray) .bindDiffuseTexture(diffuseArray) .bindSpecularTexture(specularArray); - if(flag != PhongGL::Flag::UniformBuffers && data.layer != 0) + if(!(flag & PhongGL::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else #endif @@ -2450,7 +2643,12 @@ template void PhongGLTest::renderSinglePixelTextured() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) @@ -2526,6 +2724,19 @@ template void PhongGLTest::renderTextured() { #endif #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2550,7 +2761,7 @@ template void PhongGLTest::renderTextured() { PhongGL::Flags flags = data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + if(flag & PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= PhongGL::Flag::TextureTransformation; } @@ -2686,7 +2897,12 @@ template void PhongGLTest::renderTextured() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -2762,6 +2978,19 @@ template void PhongGLTest::renderTexturedNormal() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2798,7 +3027,7 @@ template void PhongGLTest::renderTexturedNormal() { PhongGL::Flags flags = PhongGL::Flag::NormalTexture|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 - if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + if(flag & PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= PhongGL::Flag::TextureTransformation; } @@ -2877,7 +3106,12 @@ template void PhongGLTest::renderTexturedNormal() { .draw(plane); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -2958,6 +3192,19 @@ template void PhongGLTest::renderTexturedNormal() { template void PhongGLTest::renderVertexColor() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -3029,7 +3276,12 @@ template void PhongGLTest::renderVertexColor() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -3089,6 +3341,19 @@ template void PhongGLTest::renderShininess() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3120,7 +3385,12 @@ template void PhongGLTest::renderShininess() { .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -3243,6 +3513,19 @@ template void PhongGLTest::renderAlpha() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3327,7 +3610,12 @@ template void PhongGLTest::renderAlpha() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -3396,6 +3684,19 @@ template void PhongGLTest::renderObjectId() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3423,7 +3724,7 @@ template void PhongGLTest::renderObjectId() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, sphereFlags)); PhongGL::Flags flags = data.flags|flag; - if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + if(flag & PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); flags |= PhongGL::Flag::TextureTransformation; } @@ -3448,7 +3749,7 @@ template void PhongGLTest::renderObjectId() { .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) .setSubImage(0, {0, 0, data.layer}, image); shader.bindObjectIdTexture(textureArray); - if(flag != PhongGL::Flag::UniformBuffers && data.layer != 0) + if(!(flag & PhongGL::Flag::UniformBuffers) && data.layer != 0) shader.setTextureLayer(data.layer); /* to verify the default */ } else { texture = GL::Texture2D{}; @@ -3485,7 +3786,12 @@ template void PhongGLTest::renderObjectId() { .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) .setObjectId(40006) .draw(sphere); - } else if(flag == PhongGL::Flag::UniformBuffers) { + } else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -3572,6 +3878,19 @@ template void PhongGLTest::renderLights() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -3614,7 +3933,12 @@ template void PhongGLTest::renderLights() { .draw(plane); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(80.0_degf, 1.0f, 0.1f, 20.0f) @@ -3793,6 +4117,18 @@ void PhongGLTest::renderLightCulling() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= PhongGL::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); @@ -3828,7 +4164,7 @@ void PhongGLTest::renderLightCulling() { GL::Buffer lightUniform{lights}; PhongGL shader{PhongGL::Configuration{} - .setFlags(PhongGL::Flag::UniformBuffers|PhongGL::Flag::LightCulling) + .setFlags(PhongGL::Flag::UniformBuffers|PhongGL::Flag::LightCulling|data.flags) .setLightCount(data.count, data.perDrawCount)}; shader .bindProjectionBuffer(projectionUniform) @@ -3866,8 +4202,24 @@ template void PhongGLTest::renderZeroLights() { CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); #ifndef MAGNUM_TARGET_GLES2 - if(flag == PhongGL::Flag::UniformBuffers) { - setTestCaseTemplateName("Flag::UniformBuffers"); + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif + if(flag >= PhongGL::Flag::UniformBuffers) { + if(flag == PhongGL::Flag::UniformBuffers) + setTestCaseTemplateName("Flag::UniformBuffers"); + else + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -3957,7 +4309,12 @@ template void PhongGLTest::renderZeroLights() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -4051,6 +4408,19 @@ template void PhongGLTest::renderSkinning() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4128,7 +4498,12 @@ template void PhongGLTest::renderSkinning() { .setAmbientColor(0xffffff_rgbf) .setTransformationMatrix(Matrix4::scaling(Vector3{0.5f})) .draw(mesh); - } else if(flag == PhongGL::Flag::UniformBuffers) { + } else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -4188,6 +4563,19 @@ template void PhongGLTest::renderInstanced() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4504,7 +4892,12 @@ template void PhongGLTest::renderInstanced() { shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == PhongGL::Flag::UniformBuffers) { + else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{}.setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) @@ -4612,6 +5005,19 @@ template void PhongGLTest::renderInstancedSkinning() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(flag == PhongGL::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -4697,7 +5103,12 @@ template void PhongGLTest::renderInstancedSkinning() { .setAmbientColor(0xffffff_rgbf) .setTransformationMatrix(Matrix4::scaling(Vector3{0.3f})) .draw(mesh); - } else if(flag == PhongGL::Flag::UniformBuffers) { + } else if(flag == PhongGL::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == PhongGL::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { ProjectionUniform3D{} }}; @@ -4758,6 +5169,18 @@ void PhongGLTest::renderMulti() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= PhongGL::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -4999,18 +5422,18 @@ void PhongGLTest::renderMulti() { /* Material / light offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = PhongDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setLightOffsetCount(data.drawCount == 1 ? 0 : 1, 2) + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setLightOffsetCount(data.bindWithOffset ? 0 : 1, 2) .setNormalMatrix(transformationData[0*data.uniformIncrement].transformationMatrix.normalMatrix()) .setObjectId(1211); drawData[1*data.uniformIncrement] = PhongDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) - .setLightOffsetCount(data.drawCount == 1 ? 0 : 3, 1) + .setMaterialId(data.bindWithOffset ? 0 : 0) + .setLightOffsetCount(data.bindWithOffset ? 0 : 3, 1) .setNormalMatrix(transformationData[1*data.uniformIncrement].transformationMatrix.normalMatrix()) .setObjectId(5627); drawData[2*data.uniformIncrement] = PhongDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setLightOffsetCount(data.drawCount == 1 ? 0 : 0, 1) + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setLightOffsetCount(data.bindWithOffset ? 0 : 0, 1) .setNormalMatrix(transformationData[2*data.uniformIncrement].transformationMatrix.normalMatrix()) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; @@ -5027,8 +5450,8 @@ void PhongGLTest::renderMulti() { }) .clearColor(1, Vector4ui{27}); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(PhongMaterialUniform), sizeof(PhongMaterialUniform)); @@ -5156,6 +5579,18 @@ void PhongGLTest::renderMultiSkinning() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= PhongGL::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -5292,22 +5727,22 @@ void PhongGLTest::renderMultiSkinning() { /* Material / joint offsets are zero if we have single draw, as those are done with UBO offset bindings instead */ drawData[0*data.uniformIncrement] = PhongDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 0); drawData[1*data.uniformIncrement] = PhongDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setMaterialId(data.bindWithOffset ? 0 : 0) /* Overlaps with the first joint set with two matrices, unless the padding in the single-draw case prevents that */ - .setJointOffset(data.drawCount == 1 ? 0 : 2); + .setJointOffset(data.bindWithOffset ? 0 : 2); drawData[2*data.uniformIncrement] = PhongDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1) - .setJointOffset(data.drawCount == 1 ? 0 : 6); + .setMaterialId(data.bindWithOffset ? 0 : 1) + .setJointOffset(data.bindWithOffset ? 0 : 6); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; shader.bindProjectionBuffer(projectionUniform); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(PhongMaterialUniform), sizeof(PhongMaterialUniform)); @@ -5316,7 +5751,7 @@ void PhongGLTest::renderMultiSkinning() { sizeof(TransformationUniform3D)); shader.bindJointBuffer(jointUniform, 0*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(PhongDrawUniform), sizeof(PhongDrawUniform)); @@ -5330,7 +5765,7 @@ void PhongGLTest::renderMultiSkinning() { sizeof(TransformationUniform3D)); shader.bindJointBuffer(jointUniform, 1*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(PhongDrawUniform), sizeof(PhongDrawUniform)); @@ -5344,7 +5779,7 @@ void PhongGLTest::renderMultiSkinning() { sizeof(TransformationUniform3D)); shader.bindJointBuffer(jointUniform, 2*data.uniformIncrement*sizeof(TransformationUniform3D), - data.jointCount*sizeof(TransformationUniform3D)); + 4*sizeof(TransformationUniform3D)); shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(PhongDrawUniform), sizeof(PhongDrawUniform)); diff --git a/src/Magnum/Shaders/Test/PhongGL_Test.cpp b/src/Magnum/Shaders/Test/PhongGL_Test.cpp index f655894c4..95aafe7cc 100644 --- a/src/Magnum/Shaders/Test/PhongGL_Test.cpp +++ b/src/Magnum/Shaders/Test/PhongGL_Test.cpp @@ -55,15 +55,12 @@ const struct { UnsignedInt count, perDrawCount; const char* message; } ConfigurationSetLightCountInvalidData[] { - {"per-draw count larger than count", - 10, 11, - "per-draw light count expected to be not larger than total count of 10, got 11"}, {"count but no per-draw count", 10, 0, - "count has to be non-zero iff per-draw count is non-zero"}, - {"per-draw count but no count", - 0, 2, - "count has to be non-zero iff per-draw count is non-zero"}, + "count has to be zero if per-draw count is zero"}, + /* The rest depends on flags being set and is thus verified in constructor, + tested in PhongGLTest::constructInvalid() and + constructUniformBuffersInvalid() */ }; #ifndef MAGNUM_TARGET_GLES2 @@ -80,13 +77,10 @@ const struct { "expected at most 4 secondary per-vertex joints, got 5"}, {"joint count but no per-vertex joint count", 10, 0, 0, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, - {"per-vertex joint count but no joint count", - 0, 2, 0, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, - {"secondary per-vertex joint count but no joint count", - 0, 0, 3, - "count has to be non-zero iff (secondary) per-vertex joint count is non-zero"}, + "count has to be zero if per-vertex joint count is zero"}, + /* The rest depends on flags being set and is thus verified in constructor, + tested in PhongGLTest::constructInvalid() and + constructUniformBuffersInvalid() */ }; #endif @@ -195,12 +189,24 @@ void PhongGL_Test::debugFlagsSupersets() { } #ifndef MAGNUM_TARGET_GLES2 - /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ { std::ostringstream out; Debug{&out} << (PhongGL::Flag::MultiDraw|PhongGL::Flag::UniformBuffers); CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::MultiDraw\n"); } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::MultiDraw|PhongGL::Flag::ShaderStorageBuffers|PhongGL::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::MultiDraw|Shaders::PhongGL::Flag::ShaderStorageBuffers\n"); + } + #endif #endif } diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 20321bfe0..560b8d72a 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -186,7 +186,10 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case and 3+1 in 2D, per-material 3 */ {"multiple materials, draws", VectorGL2D::Flag::UniformBuffers, 15, 42}, - {"multidraw with all the things", VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 15, 42} + {"multidraw with all the things", VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 15, 42}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with all the things", VectorGL2D::Flag::ShaderStorageBuffers|VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 0, 0} + #endif }; #endif @@ -197,6 +200,7 @@ constexpr struct { UnsignedInt materialCount, drawCount; const char* message; } ConstructUniformBuffersInvalidData[]{ + /* These two fail for UBOs but not SSBOs */ {"zero draws", VectorGL2D::Flag::UniformBuffers, 1, 0, "draw count can't be zero"}, {"zero materials", VectorGL2D::Flag::UniformBuffers, 0, 1, @@ -228,21 +232,40 @@ constexpr struct { const char* expected3D; VectorGL2D::Flags flags; UnsignedInt materialCount, drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - {}, 1, 1, 16, + {}, 1, 1, true, 16, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::ShaderStorageBuffers, 0, 0, true, 16, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, + #endif {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - {}, 2, 3, 1, + {}, 2, 3, false, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::ShaderStorageBuffers, 0, 0, false, 1, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, + #endif {"multidraw", "multidraw2D.tga", "multidraw3D.tga", - VectorGL2D::Flag::MultiDraw, 2, 3, 1, + VectorGL2D::Flag::MultiDraw, 2, 3, false, 1, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::ShaderStorageBuffers|VectorGL2D::Flag::MultiDraw, 0, 0, false, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, + #endif }; #endif @@ -308,10 +331,16 @@ VectorGLTest::VectorGLTest() { &VectorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_WEBGL + &VectorGLTest::renderDefaults2D, + #endif #endif &VectorGLTest::renderDefaults3D, #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_WEBGL + &VectorGLTest::renderDefaults3D, + #endif #endif }, &VectorGLTest::renderSetup, @@ -322,10 +351,16 @@ VectorGLTest::VectorGLTest() { &VectorGLTest::render2D, #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::render2D, + #ifndef MAGNUM_TARGET_WEBGL + &VectorGLTest::render2D, + #endif #endif &VectorGLTest::render3D, #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::render3D, + #ifndef MAGNUM_TARGET_WEBGL + &VectorGLTest::render3D, + #endif #endif }, Containers::arraySize(RenderData), @@ -421,6 +456,18 @@ template void VectorGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= VectorGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= VectorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -727,6 +774,19 @@ constexpr GL::TextureFormat TextureFormatR = template void VectorGLTest::renderDefaults2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VectorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -770,7 +830,12 @@ template void VectorGLTest::renderDefaults2D() { shader.draw(square); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VectorGL2D::Flag::UniformBuffers) { + else if(flag == VectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -807,6 +872,19 @@ template void VectorGLTest::renderDefaults2D() { template void VectorGLTest::renderDefaults3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VectorGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -850,7 +928,12 @@ template void VectorGLTest::renderDefaults3D() { shader.draw(plane); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VectorGL2D::Flag::UniformBuffers) { + else if(flag == VectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -890,6 +973,19 @@ template void VectorGLTest::render2D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VectorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -940,7 +1036,12 @@ template void VectorGLTest::render2D() { shader.draw(square); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VectorGL2D::Flag::UniformBuffers) { + else if(flag == VectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -996,6 +1097,19 @@ template void VectorGLTest::render3D() { setTestCaseDescription(data.name); #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VectorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName("Flag::ShaderStorageBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VectorGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1048,7 +1162,12 @@ template void VectorGLTest::render3D() { shader.draw(plane); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VectorGL3D::Flag::UniformBuffers) { + else if(flag == VectorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VectorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -1111,6 +1230,18 @@ void VectorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= VectorGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= VectorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1217,11 +1348,11 @@ void VectorGLTest::renderMulti2D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = VectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1); + .setMaterialId(data.bindWithOffset ? 0 : 1); drawData[1*data.uniformIncrement] = VectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 0); drawData[2*data.uniformIncrement] = VectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1); + .setMaterialId(data.bindWithOffset ? 0 : 1); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; VectorGL2D shader{VectorGL2D::Configuration{} @@ -1230,8 +1361,8 @@ void VectorGLTest::renderMulti2D() { .setDrawCount(data.drawCount)}; shader.bindVectorTexture(vector); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(VectorMaterialUniform), sizeof(VectorMaterialUniform)); @@ -1315,6 +1446,18 @@ void VectorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= VectorGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= VectorGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1426,11 +1569,11 @@ void VectorGLTest::renderMulti3D() { /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = VectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1); + .setMaterialId(data.bindWithOffset ? 0 : 1); drawData[1*data.uniformIncrement] = VectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 0); + .setMaterialId(data.bindWithOffset ? 0 : 0); drawData[2*data.uniformIncrement] = VectorDrawUniform{} - .setMaterialId(data.drawCount == 1 ? 0 : 1); + .setMaterialId(data.bindWithOffset ? 0 : 1); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; VectorGL3D shader{VectorGL3D::Configuration{} @@ -1439,8 +1582,8 @@ void VectorGLTest::renderMulti3D() { .setDrawCount(data.drawCount)}; shader.bindVectorTexture(vector); - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, 1*data.uniformIncrement*sizeof(VectorMaterialUniform), sizeof(VectorMaterialUniform)); diff --git a/src/Magnum/Shaders/Test/VectorGL_Test.cpp b/src/Magnum/Shaders/Test/VectorGL_Test.cpp index dd8754bde..7ab82e2c1 100644 --- a/src/Magnum/Shaders/Test/VectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VectorGL_Test.cpp @@ -99,10 +99,24 @@ void VectorGL_Test::debugFlags() { #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"); + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ + { + std::ostringstream out; + Debug{&out} << (VectorGL3D::Flag::MultiDraw|VectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::MultiDraw\n"); + } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (VectorGL2D::Flag::ShaderStorageBuffers|VectorGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (VectorGL3D::Flag::MultiDraw|VectorGL3D::Flag::ShaderStorageBuffers|VectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::MultiDraw|Shaders::VectorGL::Flag::ShaderStorageBuffers\n"); + } + #endif } #endif diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 1b7fa174b..63a62b1d9 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -164,7 +164,10 @@ constexpr struct { /* 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}, - {"multidraw with all the things", VertexColorGL2D::Flag::MultiDraw, 63} + {"multidraw with all the things", VertexColorGL2D::Flag::MultiDraw, 63}, + #ifndef MAGNUM_TARGET_WEBGL + {"shader storage + multidraw with all the things", VertexColorGL2D::Flag::ShaderStorageBuffers|VertexColorGL2D::Flag::MultiDraw, 0} + #endif }; #endif @@ -175,21 +178,40 @@ constexpr struct { const char* expected3D; VertexColorGL2D::Flags flags; UnsignedInt drawCount; + bool bindWithOffset; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - {}, 1, 16, + {}, 1, true, 16, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VertexColorGL2D::Flag::ShaderStorageBuffers, 1, true, 16, /* Minor differences on ARM Mali */ 0.34f, 0.01f}, + #endif {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - {}, 3, 1, + {}, 3, false, 1, /* Minor differences on ARM Mali */ 0.34f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VertexColorGL2D::Flag::ShaderStorageBuffers, 3, false, 1, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f}, + #endif {"multidraw", "multidraw2D.tga", "multidraw3D.tga", - VertexColorGL2D::Flag::MultiDraw, 3, 1, + VertexColorGL2D::Flag::MultiDraw, 3, false, 1, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VertexColorGL2D::Flag::ShaderStorageBuffers|VertexColorGL2D::Flag::MultiDraw, 0, false, 1, /* Minor differences on ARM Mali */ 0.34f, 0.01f} + #endif }; #endif @@ -240,35 +262,59 @@ VertexColorGLTest::VertexColorGLTest() { &VertexColorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::renderDefaults2D, + #endif #endif &VertexColorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::renderDefaults2D, + #endif #endif &VertexColorGLTest::renderDefaults3D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::renderDefaults3D, + #endif #endif &VertexColorGLTest::renderDefaults3D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::renderDefaults3D, + #endif #endif &VertexColorGLTest::render2D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::render2D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::render2D, + #endif #endif &VertexColorGLTest::render2D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::render2D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::render2D, + #endif #endif &VertexColorGLTest::render3D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::render3D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::render3D, + #endif #endif &VertexColorGLTest::render3D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::render3D, + #ifndef MAGNUM_TARGET_WEBGL + &VertexColorGLTest::render3D, + #endif #endif }, &VertexColorGLTest::renderSetup, @@ -355,6 +401,18 @@ template void VertexColorGLTest::constructUniformBuffers CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= VertexColorGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -479,6 +537,7 @@ template void VertexColorGLTest::constructUniformBuffers CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + /* This fails for UBOs but not SSBOs */ std::ostringstream out; Error redirectError{&out}; VertexColorGL{typename VertexColorGL::Configuration{} @@ -580,6 +639,19 @@ void VertexColorGLTest::renderTeardown() { template void VertexColorGLTest::renderDefaults2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VertexColorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VertexColorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -611,7 +683,12 @@ template void VertexColorGLTest::renderDefa shader.draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VertexColorGL2D::Flag::UniformBuffers) { + else if(flag == VertexColorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VertexColorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} }}; @@ -643,6 +720,19 @@ template void VertexColorGLTest::renderDefa template void VertexColorGLTest::renderDefaults3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VertexColorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VertexColorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -678,7 +768,12 @@ template void VertexColorGLTest::renderDefa shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VertexColorGL3D::Flag::UniformBuffers) { + else if(flag == VertexColorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VertexColorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} }}; @@ -706,6 +801,19 @@ template void VertexColorGLTest::renderDefa template void VertexColorGLTest::render2D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VertexColorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VertexColorGL2D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -740,7 +848,12 @@ template void VertexColorGLTest::render2D() .draw(circle); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VertexColorGL2D::Flag::UniformBuffers) { + else if(flag == VertexColorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VertexColorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform2D{} .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -774,6 +887,19 @@ template void VertexColorGLTest::render2D() template void VertexColorGLTest::render3D() { #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(flag == VertexColorGL2D::Flag::ShaderStorageBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::ShaderStorageBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } else + #endif if(flag == VertexColorGL3D::Flag::UniformBuffers) { setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); @@ -812,7 +938,12 @@ template void VertexColorGLTest::render3D() .draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 - else if(flag == VertexColorGL3D::Flag::UniformBuffers) { + else if(flag == VertexColorGL2D::Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + || flag == VertexColorGL2D::Flag::ShaderStorageBuffers + #endif + ) { + /* Target hints matter just on WebGL (which doesn't have SSBOs) */ GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -859,6 +990,18 @@ void VertexColorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= VertexColorGL2D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -929,8 +1072,8 @@ void VertexColorGLTest::renderMulti2D() { .setFlags(VertexColorGL2D::Flag::UniformBuffers|data.flags) .setDrawCount(data.drawCount)}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -989,6 +1132,18 @@ void VertexColorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= VertexColorGL3D::Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_storage_buffer_object::string() << "is not supported."); + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES310)) + CORRADE_SKIP(GL::Version::GLES310 << "is not supported."); + #endif + } + #endif + if(data.flags >= VertexColorGL3D::Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -1062,8 +1217,8 @@ void VertexColorGLTest::renderMulti3D() { .setFlags(VertexColorGL3D::Flag::UniformBuffers|data.flags) .setDrawCount(data.drawCount)}; - /* Just one draw, rebinding UBOs each time */ - if(data.drawCount == 1) { + /* Rebinding UBOs / SSBOs each time */ + if(data.bindWithOffset) { shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), sizeof(TransformationProjectionUniform3D)); diff --git a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp index 40be500e0..e03337c0e 100644 --- a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp @@ -108,10 +108,24 @@ void VertexColorGL_Test::debugFlags() { #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"); + /* MultiDraw and ShaderStorageBuffers are a superset of UniformBuffers so + only one should be printed, but if there are both then both should be */ + { + std::ostringstream out; + Debug{&out} << (VertexColorGL3D::Flag::MultiDraw|VertexColorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::MultiDraw\n"); + } + #ifndef MAGNUM_TARGET_WEBGL + { + std::ostringstream out; + Debug{&out} << (VertexColorGL2D::Flag::ShaderStorageBuffers|VertexColorGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::ShaderStorageBuffers\n"); + } { + std::ostringstream out; + Debug{&out} << (VertexColorGL3D::Flag::MultiDraw|VertexColorGL3D::Flag::ShaderStorageBuffers|VertexColorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::MultiDraw|Shaders::VertexColorGL::Flag::ShaderStorageBuffers\n"); + } + #endif } #endif diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 9a5516312..c00383621 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -23,6 +23,10 @@ DEALINGS IN THE SOFTWARE. */ +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifndef NEW_GLSL #define in varying #define fragmentColor gl_FragColor @@ -50,11 +54,24 @@ uniform lowp vec4 color #endif ; -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else +/* For SSBOs, the per-draw and material arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define MATERIAL_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + #ifndef MULTI_DRAW -#if DRAW_COUNT > 1 +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -75,11 +92,11 @@ struct DrawUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 2 #endif -) uniform Draw { - DrawUniform draws[DRAW_COUNT]; +) BUFFER_OR_UNIFORM Draw { + BUFFER_READONLY DrawUniform draws[DRAW_COUNT]; }; struct MaterialUniform { @@ -89,11 +106,11 @@ struct MaterialUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 4 #endif -) uniform Material { - MaterialUniform materials[MATERIAL_COUNT]; +) BUFFER_OR_UNIFORM Material { + BUFFER_READONLY MaterialUniform materials[MATERIAL_COUNT]; }; #endif @@ -123,7 +140,9 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - #if MATERIAL_COUNT > 1 + /* With SSBOs MATERIAL_COUNT is defined to be empty, +0 makes the condition + not cause a compile error */ + #if defined(SHADER_STORAGE_BUFFERS) || MATERIAL_COUNT+0 > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #else #define materialId 0u diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index 251614ccc..2d7187b2f 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -23,6 +23,10 @@ DEALINGS IN THE SOFTWARE. */ +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -73,10 +77,22 @@ uniform mediump mat3 textureMatrix ; #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else -#if DRAW_COUNT > 1 +/* For SSBOs, the per-draw arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -90,11 +106,11 @@ uniform highp uint drawOffset #endif layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform TransformationProjection { - highp +) BUFFER_OR_UNIFORM TransformationProjection { + BUFFER_READONLY highp #ifdef TWO_DIMENSIONS /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ @@ -115,11 +131,11 @@ struct TextureTransformationUniform { }; layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 3 #endif -) uniform TextureTransformation { - TextureTransformationUniform textureTransformations[DRAW_COUNT]; +) BUFFER_OR_UNIFORM TextureTransformation { + BUFFER_READONLY TextureTransformationUniform textureTransformations[DRAW_COUNT]; }; #endif #endif diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index 2288e961f..60a6272ec 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -68,17 +68,31 @@ namespace { } template typename VectorGL::CompileState VectorGL::compile(const Configuration& configuration) { - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), - "Shaders::VectorGL: material count can't be zero", CompileState{NoCreate}); - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::VectorGL: draw count can't be zero", CompileState{NoCreate}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_NO_ASSERT) + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::VectorGL: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::VectorGL: draw count can't be zero", CompileState{NoCreate}); + } #endif #ifndef MAGNUM_TARGET_GLES if(configuration.flags() >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES @@ -115,10 +129,21 @@ template typename VectorGL::CompileState Vec .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n"_s : "#define THREE_DIMENSIONS\n"_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - configuration.drawCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + configuration.drawCount())); + } vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -129,12 +154,23 @@ template typename VectorGL::CompileState Vec GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define MATERIAL_COUNT {}\n", - configuration.drawCount(), - configuration.materialCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + frag.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + frag.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + } frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -205,7 +241,11 @@ template VectorGL::VectorGL(CompileState&& s { #ifndef MAGNUM_TARGET_GLES2 if(_flags >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -225,7 +265,12 @@ template VectorGL::VectorGL(CompileState&& s { setUniform(uniformLocation("vectorTexture"_s), TextureUnit); #ifndef MAGNUM_TARGET_GLES2 - if(_flags >= Flag::UniformBuffers) { + /* SSBOs have bindings defined in the source always */ + if(_flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(_flags >= Flag::ShaderStorageBuffers) + #endif + ) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"_s), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); if(_flags & Flag::TextureTransformation) @@ -310,37 +355,62 @@ template VectorGL& VectorGL::set template VectorGL& VectorGL::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= Flag::ShaderStorageBuffers || offset < _drawCount, + "Shaders::VectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::VectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + #endif + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || _flags >= Flag::ShaderStorageBuffers + #endif + ) setUniform(_drawOffsetUniform, offset); return *this; } template VectorGL& VectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); return *this; } template VectorGL& VectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); return *this; } template VectorGL& VectorGL::bindDrawBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding); return *this; } template VectorGL& VectorGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); return *this; } @@ -349,7 +419,11 @@ template VectorGL& VectorGL::bin "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); return *this; } @@ -358,21 +432,33 @@ template VectorGL& VectorGL::bin "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } template VectorGL& VectorGL::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding); return *this; } template VectorGL& VectorGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); return *this; } #endif @@ -388,6 +474,14 @@ template class MAGNUM_SHADERS_EXPORT VectorGL<3>; namespace Implementation { Debug& operator<<(Debug& debug, const VectorGLFlag value) { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* Special case coming from the Flags printer. As both flags are a superset + of UniformBuffers, printing just one would result in + `Flag::MultiDraw|Flag(0x8)` in the output. */ + if(value == VectorGLFlag(UnsignedByte(VectorGLFlag::MultiDraw|VectorGLFlag::ShaderStorageBuffers))) + return debug << VectorGLFlag::MultiDraw << Debug::nospace << "|" << Debug::nospace << VectorGLFlag::ShaderStorageBuffers; + #endif + debug << "Shaders::VectorGL::Flag" << Debug::nospace; switch(value) { @@ -396,6 +490,9 @@ Debug& operator<<(Debug& debug, const VectorGLFlag value) { _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) #endif #undef _c @@ -409,7 +506,16 @@ Debug& operator<<(Debug& debug, const VectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::VectorGL::Flags{}", { VectorGLFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers, meaning printing just one + would result in `Flag::MultiDraw|Flag(0x8)` in the output. So we + pass both and let the Flag printer deal with that. */ + VectorGLFlag(UnsignedByte(VectorGLFlag::MultiDraw|VectorGLFlag::ShaderStorageBuffers)), + #endif VectorGLFlag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + VectorGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif VectorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index d8028f62f..a8c15013d 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -46,6 +46,9 @@ namespace Implementation { TextureTransformation = 1 << 0, #ifndef MAGNUM_TARGET_GLES2 UniformBuffers = 1 << 1, + #ifndef MAGNUM_TARGET_WEBGL + ShaderStorageBuffers = UniformBuffers|(1 << 3), + #endif MultiDraw = UniformBuffers|(1 << 2) #endif }; @@ -172,6 +175,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @ref bindTransformationProjectionBuffer(), * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer() * and @ref bindMaterialBuffer() instead of direct uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -181,6 +185,23 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL */ UniformBuffers = 1 << 1, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @ref Configuration::setMaterialCount() and + * @relativeref{Configuration,setDrawCount()} in exchange for + * potentially more costly access and narrower platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 3), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the @@ -337,7 +358,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * * Statically defined size of the @ref VectorMaterialUniform uniform * buffer bound with @ref bindMaterialBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setMaterialCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -354,7 +376,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @ref TextureTransformationUniform uniform buffers bound with * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and * @ref bindTextureTransformationBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -429,7 +452,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -461,7 +484,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL VectorGL& setDrawOffset(UnsignedInt offset); /** - * @brief Bind a transformation and projection uniform buffer + * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -482,7 +505,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL VectorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a draw uniform buffer + * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -503,7 +526,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL VectorGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a texture transformation uniform buffer + * @brief Bind a texture transformation uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -523,7 +546,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL VectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /** - * @brief Bind a material uniform buffer + * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -626,10 +649,11 @@ template class VectorGL::Configuration { * @ref VectorMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(VectorMaterialUniform) @ce has to be - * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The per-draw materials are then specified via - * @ref VectorDrawUniform::materialId. Default value is @cpp 1 @ce. + * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), + * if @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The per-draw materials are + * specified via @ref VectorDrawUniform::materialId. Default value is + * @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), @@ -665,10 +689,10 @@ template class VectorGL::Configuration { * @cpp count*sizeof(TransformationProjectionUniform3D) @ce, * @cpp count*sizeof(VectorDrawUniform) @ce and * @cpp count*sizeof(TextureTransformationUniform) @ce has to be within - * @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffer is + * unbounded and @p count is ignored. The draw offset is then set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), diff --git a/src/Magnum/Shaders/VertexColor.vert b/src/Magnum/Shaders/VertexColor.vert index e50229641..aee08bbeb 100644 --- a/src/Magnum/Shaders/VertexColor.vert +++ b/src/Magnum/Shaders/VertexColor.vert @@ -23,6 +23,10 @@ DEALINGS IN THE SOFTWARE. */ +#if defined(SHADER_STORAGE_BUFFERS) && !defined(GL_ES) +#extension GL_ARB_shader_storage_buffer_object: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -62,10 +66,22 @@ uniform highp mat4 transformationProjectionMatrix #error #endif -/* Uniform buffers */ +/* Uniform / shader storage buffers */ #else -#if DRAW_COUNT > 1 +/* For SSBOs, the per-draw arrays are unbounded */ +#ifdef SHADER_STORAGE_BUFFERS +#define DRAW_COUNT +#define BUFFER_OR_UNIFORM buffer +#define BUFFER_READONLY readonly +#else +#define BUFFER_OR_UNIFORM uniform +#define BUFFER_READONLY +#endif + +/* With SSBOs DRAW_COUNT is defined to be empty, +0 makes the condition not + cause a compile error */ +#if defined(SHADER_STORAGE_BUFFERS) || DRAW_COUNT+0 > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -79,11 +95,11 @@ uniform highp uint drawOffset #endif layout(std140 - #ifdef EXPLICIT_BINDING + #if defined(EXPLICIT_BINDING) || defined(SHADER_STORAGE_BUFFERS) , binding = 1 #endif -) uniform TransformationProjection { - highp +) BUFFER_OR_UNIFORM TransformationProjection { + BUFFER_READONLY highp #ifdef TWO_DIMENSIONS /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for details */ diff --git a/src/Magnum/Shaders/VertexColorGL.cpp b/src/Magnum/Shaders/VertexColorGL.cpp index cf51a6935..3c8d31b6b 100644 --- a/src/Magnum/Shaders/VertexColorGL.cpp +++ b/src/Magnum/Shaders/VertexColorGL.cpp @@ -61,15 +61,29 @@ namespace { } template typename VertexColorGL::CompileState VertexColorGL::compile(const Configuration& configuration) { - #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), - "Shaders::VertexColorGL: draw count can't be zero", CompileState{NoCreate}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(CORRADE_NO_ASSERT) + #ifndef MAGNUM_TARGET_WEBGL + if(!(configuration.flags() >= Flag::ShaderStorageBuffers)) + #endif + { + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::VertexColorGL: draw count can't be zero", CompileState{NoCreate}); + } #endif #ifndef MAGNUM_TARGET_GLES if(configuration.flags() >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_storage_buffer_object); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES310); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::MultiDraw) { #ifndef MAGNUM_TARGET_GLES @@ -105,10 +119,21 @@ template typename VertexColorGL::CompileStat vert.addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n"_s : "#define THREE_DIMENSIONS\n"_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::format( - "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - configuration.drawCount())); + #ifndef MAGNUM_TARGET_WEBGL + /* SSBOs have unbounded per-draw arrays so just a plain string can be + passed */ + if(configuration.flags() >= Flag::ShaderStorageBuffers) { + vert.addSource( + "#define UNIFORM_BUFFERS\n" + "#define SHADER_STORAGE_BUFFERS\n"_s); + } else + #endif + { + vert.addSource(Utility::format( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + configuration.drawCount())); + } vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); } #endif @@ -182,7 +207,11 @@ template VertexColorGL::VertexColorGL(Compil { #ifndef MAGNUM_TARGET_GLES2 if(_flags >= Flag::UniformBuffers) { - if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"_s); + if(_drawCount > 1 + #ifndef MAGNUM_TARGET_WEBGL + || flags() >= Flag::ShaderStorageBuffers + #endif + ) _drawOffsetUniform = uniformLocation("drawOffset"_s); } else #endif { @@ -191,7 +220,11 @@ template VertexColorGL::VertexColorGL(Compil } #ifndef MAGNUM_TARGET_GLES2 + /* SSBOs have bindings defined in the source always */ if(_flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_WEBGL + && !(_flags >= Flag::ShaderStorageBuffers) + #endif #ifndef MAGNUM_TARGET_GLES && !context.isExtensionSupported(state._version) #elif !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -243,8 +276,13 @@ template VertexColorGL& VertexColorGL VertexColorGL& VertexColorGL::setDrawOffset(const UnsignedInt offset) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VertexColorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_ASSERT(_flags >= Flag::ShaderStorageBuffers || offset < _drawCount, + "Shaders::VertexColorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #else CORRADE_ASSERT(offset < _drawCount, "Shaders::VertexColorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + #endif if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); return *this; } @@ -252,14 +290,22 @@ template VertexColorGL& VertexColorGL VertexColorGL& VertexColorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VertexColorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); return *this; } template VertexColorGL& VertexColorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { CORRADE_ASSERT(_flags >= Flag::UniformBuffers, "Shaders::VertexColorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); - buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + buffer.bind( + #ifndef MAGNUM_TARGET_WEBGL + _flags >= Flag::ShaderStorageBuffers ? GL::Buffer::Target::ShaderStorage : + #endif + GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); return *this; } #endif @@ -270,6 +316,14 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL<3>; namespace Implementation { Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* Special case coming from the Flags printer. As both flags are a superset + of UniformBuffers, printing just one would result in + `Flag::MultiDraw|Flag(0x4)` in the output. */ + if(value == VertexColorGLFlag(UnsignedByte(VertexColorGLFlag::MultiDraw|VertexColorGLFlag::ShaderStorageBuffers))) + return debug << VertexColorGLFlag::MultiDraw << Debug::nospace << "|" << Debug::nospace << VertexColorGLFlag::ShaderStorageBuffers; + #endif + debug << "Shaders::VertexColorGL::Flag" << Debug::nospace; switch(value) { @@ -277,6 +331,9 @@ Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { #define _c(v) case VertexColorGLFlag::v: return debug << "::" #v; #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + #ifndef MAGNUM_TARGET_WEBGL + _c(ShaderStorageBuffers) + #endif _c(MultiDraw) #endif #undef _c @@ -289,7 +346,16 @@ 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 + #ifndef MAGNUM_TARGET_WEBGL + /* Both are a superset of UniformBuffers, meaning printing just one + would result in `Flag::MultiDraw|Flag(0x4)` in the output. So we + pass both and let the Flag printer deal with that. */ + VertexColorGLFlag(UnsignedByte(VertexColorGLFlag::MultiDraw|VertexColorGLFlag::ShaderStorageBuffers)), + #endif VertexColorGLFlag::MultiDraw, /* Superset of UniformBuffers */ + #ifndef MAGNUM_TARGET_WEBGL + VertexColorGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ + #endif VertexColorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index 4e810fde2..019576d60 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -45,6 +45,9 @@ namespace Implementation { enum class VertexColorGLFlag: UnsignedByte { #ifndef MAGNUM_TARGET_GLES2 UniformBuffers = 1 << 0, + #ifndef MAGNUM_TARGET_WEBGL + ShaderStorageBuffers = UniformBuffers|(1 << 2), + #endif MultiDraw = UniformBuffers|(1 << 1) #endif }; @@ -165,6 +168,7 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * Use uniform buffers. Expects that uniform data are supplied via * @ref bindTransformationProjectionBuffer() instead of direct * uniform setters. + * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -174,6 +178,23 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ */ UniformBuffers = 1 << 0, + #ifndef MAGNUM_TARGET_WEBGL + /** + * Use shader storage buffers. Superset of functionality provided + * by @ref Flag::UniformBuffers, compared to it doesn't have any + * size limits on @relativeref{Configuration,setDrawCount()} in + * exchange for potentially more costly access and narrower + * platform support. + * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} + * @requires_gles31 Shader storage buffers are not available in + * OpenGL ES 3.0 and older. + * @requires_gles Shader storage buffers are not available in + * WebGL. + * @m_since_latest + */ + ShaderStorageBuffers = UniformBuffers|(1 << 2), + #endif + /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the @@ -331,7 +352,8 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D uniform buffers bound with * @ref bindTransformationProjectionBuffer(). Has use only if - * @ref Flag::UniformBuffers is set. + * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers + * is not set. * @see @ref Configuration::setDrawCount() * @requires_gles30 Not defined on OpenGL ES 2.0 builds. * @requires_webgl20 Not defined on WebGL 1.0 builds. @@ -364,7 +386,7 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ #ifndef MAGNUM_TARGET_GLES2 /** @{ - * @name Uniform buffer binding and related uniform setters + * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ @@ -393,7 +415,7 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ VertexColorGL& setDrawOffset(UnsignedInt offset); /** - * @brief Bind a transformation and projection uniform buffer + * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * @m_since_latest * @@ -480,10 +502,10 @@ template class VertexColorGL::Configuration * statically defined size and * @cpp count*sizeof(TransformationProjectionUniform2D) @ce /, * @cpp count*sizeof(TransformationProjectionUniform3D) @ce has to be - * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(). - * - * The draw offset is then set via @ref setDrawOffset(). Default value - * is @cpp 1 @ce. + * within @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if + * @ref Flag::ShaderStorageBuffers is set as well, the buffers are + * unbounded and @p count is ignored. The draw offset is set via + * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref VertexColorGL::drawCount()