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()