diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index be552430a..efa98292c 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -42,7 +42,7 @@ #ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 2) +layout(location = 3) #endif uniform lowp vec4 color #ifndef GL_ES @@ -51,12 +51,12 @@ uniform lowp vec4 color ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 3) +layout(location = 4) #endif uniform lowp vec4 outlineColor; /* defaults to zero */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 4) +layout(location = 5) #endif uniform lowp vec2 outlineRange #ifndef GL_ES @@ -65,7 +65,7 @@ uniform lowp vec2 outlineRange ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 5) +layout(location = 6) #endif uniform lowp float smoothness #ifndef GL_ES @@ -141,11 +141,23 @@ layout(std140 #ifdef EXPLICIT_BINDING layout(binding = 6) #endif -uniform lowp sampler2D vectorTexture; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + vectorTexture; /* Inputs */ -in mediump vec2 interpolatedTextureCoordinates; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #ifdef MULTI_DRAW flat in highp uint drawId; diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 7718d636b..8408ecd89 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -45,6 +45,7 @@ #include #include "Magnum/GL/Buffer.h" +#include "Magnum/GL/TextureArray.h" #endif #ifdef MAGNUM_BUILD_STATIC @@ -110,6 +111,10 @@ template typename DistanceFieldVectorGL::Com #endif } #endif + #ifndef MAGNUM_TARGET_GLES + if(configuration.flags() >= Flag::TextureArrays) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::EXT::texture_array); + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -133,6 +138,9 @@ template typename DistanceFieldVectorGL::Com GL::Shader vert{version, GL::Shader::Type::Vertex}; vert.addSource(rs.getString("compatibility.glsl"_s)) .addSource(configuration.flags() & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n"_s : ""_s) + #ifndef MAGNUM_TARGET_GLES2 + .addSource(configuration.flags() & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n"_s : ""_s) + #endif .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n"_s : "#define THREE_DIMENSIONS\n"_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { @@ -159,7 +167,11 @@ template typename DistanceFieldVectorGL::Com .submitCompile(); GL::Shader frag{version, GL::Shader::Type::Fragment}; - frag.addSource(rs.getString("compatibility.glsl"_s)); + frag.addSource(rs.getString("compatibility.glsl"_s)) + #ifndef MAGNUM_TARGET_GLES2 + .addSource(configuration.flags() & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n"_s : ""_s) + #endif + ; #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { #ifndef MAGNUM_TARGET_WEBGL @@ -259,6 +271,10 @@ template DistanceFieldVectorGL::DistanceFiel _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"_s); if(_flags & Flag::TextureTransformation) _textureMatrixUniform = uniformLocation("textureMatrix"_s); + #ifndef MAGNUM_TARGET_GLES2 + if(_flags & Flag::TextureArrays) + _textureLayerUniform = uniformLocation("textureLayer"_s); + #endif _colorUniform = uniformLocation("color"_s); _outlineColorUniform = uniformLocation("outlineColor"_s); _outlineRangeUniform = uniformLocation("outlineRange"_s); @@ -300,6 +316,7 @@ template DistanceFieldVectorGL::DistanceFiel setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); if(_flags & Flag::TextureTransformation) setTextureMatrix(Matrix3{Math::IdentityInit}); + /* Texture layer is zero by default */ setColor(Color4{1.0f}); /* Outline color is zero by default */ setOutlineRange(0.5f, 1.0f); @@ -344,6 +361,17 @@ template DistanceFieldVectorGL& DistanceFiel return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template DistanceFieldVectorGL& DistanceFieldVectorGL::setTextureLayer(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setTextureLayer(): the shader was created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::DistanceFieldVectorGL::setTextureLayer(): the shader was not created with texture arrays enabled", *this); + setUniform(_textureLayerUniform, id); + return *this; +} +#endif + template DistanceFieldVectorGL& DistanceFieldVectorGL::setColor(const Color4& color) { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), @@ -493,10 +521,25 @@ template DistanceFieldVectorGL& DistanceFiel #endif template DistanceFieldVectorGL& DistanceFieldVectorGL::bindVectorTexture(GL::Texture2D& texture) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::DistanceFieldVectorGL::bindVectorTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif texture.bind(TextureUnit); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindVectorTexture(GL::Texture2DArray& texture) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::DistanceFieldVectorGL::bindVectorTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + #endif + texture.bind(TextureUnit); + return *this; +} +#endif + template class MAGNUM_SHADERS_EXPORT DistanceFieldVectorGL<2>; template class MAGNUM_SHADERS_EXPORT DistanceFieldVectorGL<3>; @@ -523,6 +566,7 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { _c(ShaderStorageBuffers) #endif _c(MultiDraw) + _c(TextureArrays) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -545,7 +589,8 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlags value) { #ifndef MAGNUM_TARGET_WEBGL DistanceFieldVectorGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ #endif - DistanceFieldVectorGLFlag::UniformBuffers + DistanceFieldVectorGLFlag::UniformBuffers, + DistanceFieldVectorGLFlag::TextureArrays #endif }); } diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index a81ece073..8b25b2e08 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -50,7 +50,8 @@ namespace Implementation { #ifndef MAGNUM_TARGET_WEBGL ShaderStorageBuffers = UniformBuffers|(1 << 3), #endif - MultiDraw = UniformBuffers|(1 << 2) + MultiDraw = UniformBuffers|(1 << 2), + TextureArrays = 1 << 4 #endif }; typedef Containers::EnumSet DistanceFieldVectorGLFlags; @@ -89,6 +90,12 @@ Common rendering setup: @snippet Shaders-gl.cpp DistanceFieldVectorGL-usage2 +If @ref Flag::TextureArrays is enabled, pass a @ref GL::Texture2DArray instance +instead of @ref GL::Texture2D. The layer is taken from the third coordinate of +@ref TextureArrayCoordinates, if used instead of @ref TextureCoordinates, +otherwise layer @cpp 0 @ce is picked. Additionally, the value of +@ref setTextureLayer(), which is @cpp 0 @ce by default, is added to the layer. + @section Shaders-DistanceFieldVectorGL-ubo Uniform buffers See @ref shaders-usage-ubo for a high-level overview that applies to all @@ -148,10 +155,26 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @brief 2D texture coordinates * * @ref shaders-generic "Generic attribute", - * @relativeref{Magnum,Vector2}. + * @relativeref{Magnum,Vector2}. Use either this or the + * @ref TextureArrayCoordinates attribute. */ typedef typename GenericGL::TextureCoordinates TextureCoordinates; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief 2D array texture coordinates + * + * @ref shaders-generic "Generic attribute", + * @relativeref{Magnum,Vector3}. Use either this or the + * @ref TextureCoordinates attribute. The third component is used only + * if @ref Flag::TextureArrays is set. + * @requires_gl30 Extension @gl_extension{EXT,texture_array} + * @requires_gles30 Texture arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Texture arrays are not available in WebGL 1.0. + */ + typedef typename GenericGL::TextureArrayCoordinates TextureArrayCoordinates; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -233,7 +256,27 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ - MultiDraw = UniformBuffers|(1 << 2) + MultiDraw = UniformBuffers|(1 << 2), + + /** + * Use 2D texture arrays. Expects that the texture is supplied via + * @ref bindVectorTexture(GL::Texture2DArray&) instead of + * @ref bindVectorTexture(GL::Texture2D&). The layer is taken from + * the third coordinate of @ref TextureArrayCoordinates, if used + * instead of @ref TextureCoordinates, otherwise layer @cpp 0 @ce + * is picked. Additionally, if @ref Flag::UniformBuffers is not + * enabled, the value of @ref setTextureLayer() is added to the + * layer; if @ref Flag::UniformBuffers is enabled and + * @ref Flag::TextureTransformation is enabled as well, the value + * of @ref TextureTransformationUniform::layer is added to the + * layer. + * @requires_gl30 Extension @gl_extension{EXT,texture_array} + * @requires_gles30 Texture arrays are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Texture arrays are not available in WebGL 1.0. + * @m_since_latest + */ + TextureArrays = 1 << 4, #endif }; @@ -428,6 +471,28 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector */ DistanceFieldVectorGL& setTextureMatrix(const Matrix3& matrix); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Set texture array layer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::TextureArrays + * enabled. Initial value is @cpp 0 @ce. If a three-component + * @ref TextureArrayCoordinates attribute is used instead of + * @ref TextureCoordinates, this value is added to the layer coming + * from the third component. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::layer and call + * @ref bindTextureTransformationBuffer() instead. + * @requires_gl30 Extension @gl_extension{EXT,texture_array} + * @requires_gles30 Texture arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Texture arrays are not available in WebGL 1.0. + */ + DistanceFieldVectorGL& setTextureLayer(UnsignedInt layer); + #endif + /** * @brief Set fill color * @return Reference to self (for method chaining) @@ -622,10 +687,32 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @brief Bind a vector texture * @return Reference to self (for method chaining) * + * If @ref Flag::TextureArrays is enabled, use + * @ref bindVectorTexture(GL::Texture2DArray&) instead. * @see @ref Flag::TextureTransformation, @ref setTextureMatrix() */ DistanceFieldVectorGL& bindVectorTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind a vector array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::TextureArrays + * enabled. The layer is taken from the third coordinate of + * @ref TextureArrayCoordinates, if used instead of + * @ref TextureCoordinates, otherwise layer @cpp 0 @ce is picked. + * Additionally, if @ref Flag::UniformBuffers is not enabled, the layer + * index is offset with the value set in @ref setTextureLayer(); if + * @ref Flag::UniformBuffers is enabled and + * @ref Flag::TextureTransformation is enabled as well, the layer index + * is offset with @ref TextureTransformationUniform::layer. + * @see @ref Flag::TextureTransformation, @ref setTextureMatrix() + */ + DistanceFieldVectorGL& bindVectorTexture(GL::Texture2DArray& texture); + #endif + /** * @} */ @@ -643,10 +730,13 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, - _colorUniform{2}, - _outlineColorUniform{3}, - _outlineRangeUniform{4}, - _smoothnessUniform{5}; + #ifndef MAGNUM_TARGET_GLES2 + _textureLayerUniform{2}, + #endif + _colorUniform{3}, + _outlineColorUniform{4}, + _outlineRangeUniform{5}, + _smoothnessUniform{6}; #ifndef MAGNUM_TARGET_GLES2 /* Used instead of all other uniforms when Flag::UniformBuffers is set, so it can alias them */ diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 3d321f239..1003bffb3 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -63,6 +63,7 @@ #include "Magnum/GL/Extensions.h" #include "Magnum/GL/MeshView.h" +#include "Magnum/GL/TextureArray.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/Primitives/Circle.h" @@ -100,9 +101,12 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { #ifndef MAGNUM_TARGET_GLES2 template void setUniformUniformBuffersEnabled(); template void bindBufferUniformBuffersNotEnabled(); + template void bindTextureInvalid(); + template void bindTextureArrayInvalid(); #endif template void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 + template void setTextureLayerNotArray(); template void bindTextureTransformBufferNotEnabled(); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -167,7 +171,11 @@ constexpr struct { DistanceFieldVectorGL2D::Flags flags; } ConstructData[]{ {"", {}}, - {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation} + {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture arrays", DistanceFieldVectorGL2D::Flag::TextureArrays}, + {"texture transformation + texture arrays", DistanceFieldVectorGL2D::Flag::TextureTransformation|DistanceFieldVectorGL2D::Flag::TextureArrays}, + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -179,12 +187,14 @@ constexpr struct { {"classic fallback", {}, 1, 1}, {"", DistanceFieldVectorGL2D::Flag::UniformBuffers, 1, 1}, {"texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 1, 1}, + {"texture arrays", DistanceFieldVectorGL2D::Flag::TextureArrays, 1, 1}, + {"texture transformation + texture arrays", DistanceFieldVectorGL2D::Flag::TextureTransformation|DistanceFieldVectorGL2D::Flag::TextureArrays, 1, 1}, /* 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|DistanceFieldVectorGL2D::Flag::TextureArrays, 16, 48}, #ifndef MAGNUM_TARGET_WEBGL - {"shader storage + multidraw with all the things", DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation, 0, 0} + {"shader storage + multidraw with all the things", DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation|DistanceFieldVectorGL2D::Flag::TextureArrays, 0, 0} #endif }; @@ -206,22 +216,77 @@ const struct { const char* name; DistanceFieldVectorGL2D::Flags flags; Matrix3 textureTransformation; + bool arrayTextureCoordinates; + Int layerAttribute, layerUniform; Color4 color, outlineColor; Float outlineRangeStart, outlineRangeEnd, smoothness; const char* file2D; const char* file3D; bool flip; } RenderData[] { - {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation, + {"texture transformation", + DistanceFieldVectorGL2D::Flag::TextureTransformation, Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), - 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, + false, 0, 0, 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, "defaults-distancefield.tga", "defaults-distancefield.tga", true}, - {"smooth0.1", {}, {}, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + {"smooth0.1", + {}, {}, + false, 0, 0, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, - {"smooth0.2", {}, {}, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.2f, + {"smooth0.2", + {}, {}, + false, 0, 0, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.2f, "smooth0.2-2D.tga", "smooth0.2-3D.tga", false}, - {"outline", {}, {}, 0xffff99_rgbf, 0x9999ff_rgbf, 0.6f, 0.45f, 0.05f, - "outline2D.tga", "outline3D.tga", false} + {"outline", + {}, {}, + false, 0, 0, 0xffff99_rgbf, 0x9999ff_rgbf, 0.6f, 0.45f, 0.05f, + "outline2D.tga", "outline3D.tga", false}, + #ifndef MAGNUM_TARGET_GLES2 + {"array texture, 2D coordinates, first layer", + DistanceFieldVectorGL2D::Flag::TextureArrays, {}, + false, 0, 0, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"array texture, 2D coordinates, arbitrary layer from uniform", + DistanceFieldVectorGL2D::Flag::TextureArrays, {}, + false, 0, 6, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"array texture, 2D coordinates, texture transformation, arbitrary layer from uniform", + DistanceFieldVectorGL2D::Flag::TextureArrays|DistanceFieldVectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + false, 0, 6, 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, + "defaults-distancefield.tga", "defaults-distancefield.tga", true}, + {"array texture, array coordinates, first layer", + DistanceFieldVectorGL2D::Flag::TextureArrays, {}, + true, 0, 0, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"array texture, array coordinates, arbitrary layer from attribute", + DistanceFieldVectorGL2D::Flag::TextureArrays, {}, + true, 6, 0, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"array texture, array coordinates, arbitrary layer from uniform", + DistanceFieldVectorGL2D::Flag::TextureArrays, {}, + true, 0, 6, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"array texture, array coordinates, arbitrary layer from both", + DistanceFieldVectorGL2D::Flag::TextureArrays, {}, + true, 2, 4, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"array texture, array coordinates, texture transformation, arbitrary layer from attribute", + DistanceFieldVectorGL2D::Flag::TextureArrays|DistanceFieldVectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + true, 6, 0, 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, + "defaults-distancefield.tga", "defaults-distancefield.tga", true}, + {"array texture, array coordinates, texture transformation, arbitrary layer from uniform", + DistanceFieldVectorGL2D::Flag::TextureArrays|DistanceFieldVectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + true, 0, 6, 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, + "defaults-distancefield.tga", "defaults-distancefield.tga", true}, + {"array texture, array coordinates, texture transformation, arbitrary layer from both", + DistanceFieldVectorGL2D::Flag::TextureArrays|DistanceFieldVectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + true, 2, 4, 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, + "defaults-distancefield.tga", "defaults-distancefield.tga", true}, + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -239,31 +304,55 @@ constexpr struct { {}, 1, 1, true, 16, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, + {"bind with offset, texture array", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::TextureArrays, 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}, + {"bind with offset, texture array, shader storage", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::TextureArrays|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, false, 1, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, + {"draw offset, texture array", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::TextureArrays, 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}, + {"draw offset, texture array, shader storage", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::TextureArrays|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, false, 1, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, + {"multidraw, texture array", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::TextureArrays|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}, + {"multidraw, texture array, shader storage", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::TextureArrays|DistanceFieldVectorGL2D::Flag::ShaderStorageBuffers|DistanceFieldVectorGL2D::Flag::MultiDraw, 0, 0, false, 1, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, #endif }; #endif @@ -312,10 +401,16 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { &DistanceFieldVectorGLTest::setUniformUniformBuffersEnabled<3>, &DistanceFieldVectorGLTest::bindBufferUniformBuffersNotEnabled<2>, &DistanceFieldVectorGLTest::bindBufferUniformBuffersNotEnabled<3>, + &DistanceFieldVectorGLTest::bindTextureInvalid<2>, + &DistanceFieldVectorGLTest::bindTextureInvalid<3>, + &DistanceFieldVectorGLTest::bindTextureArrayInvalid<2>, + &DistanceFieldVectorGLTest::bindTextureArrayInvalid<3>, #endif &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<2>, &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<3>, #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::setTextureLayerNotArray<2>, + &DistanceFieldVectorGLTest::setTextureLayerNotArray<3>, &DistanceFieldVectorGLTest::bindTextureTransformBufferNotEnabled<2>, &DistanceFieldVectorGLTest::bindTextureTransformBufferNotEnabled<3>, #endif @@ -633,6 +728,7 @@ template void DistanceFieldVectorGLTest::setUniformUnifo Error redirectError{&out}; shader.setTransformationProjectionMatrix({}) .setTextureMatrix({}) + .setTextureLayer({}) .setColor({}) .setOutlineColor({}) .setOutlineRange({}, {}) @@ -640,6 +736,7 @@ template void DistanceFieldVectorGLTest::setUniformUnifo CORRADE_COMPARE(out, "Shaders::DistanceFieldVectorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" "Shaders::DistanceFieldVectorGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setTextureLayer(): the shader was created with uniform buffers enabled\n" "Shaders::DistanceFieldVectorGL::setColor(): the shader was created with uniform buffers enabled\n" "Shaders::DistanceFieldVectorGL::setOutlineColor(): the shader was created with uniform buffers enabled\n" "Shaders::DistanceFieldVectorGL::setOutlineRange(): the shader was created with uniform buffers enabled\n" @@ -676,6 +773,45 @@ template void DistanceFieldVectorGLTest::bindBufferUnifo "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::DistanceFieldVectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); } + +template void DistanceFieldVectorGLTest::bindTextureInvalid() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2D texture; + DistanceFieldVectorGL shader{typename DistanceFieldVectorGL::Configuration{} + .setFlags(DistanceFieldVectorGL::Flag::TextureArrays)}; + + Containers::String out; + Error redirectError{&out}; + shader.bindVectorTexture(texture); + CORRADE_COMPARE(out, "Shaders::DistanceFieldVectorGL::bindVectorTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n"); +} + +template void DistanceFieldVectorGLTest::bindTextureArrayInvalid() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2DArray texture; + DistanceFieldVectorGL shader; + + Containers::String out; + Error redirectError{&out}; + shader.bindVectorTexture(texture); + CORRADE_COMPARE(out, "Shaders::DistanceFieldVectorGL::bindVectorTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n"); +} #endif template void DistanceFieldVectorGLTest::setTextureMatrixNotEnabled() { @@ -693,6 +829,25 @@ template void DistanceFieldVectorGLTest::setTextureMatri } #ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::setTextureLayerNotArray() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2D texture; + DistanceFieldVectorGL shader; + + Containers::String out; + Error redirectError{&out}; + shader.setTextureLayer(37); + CORRADE_COMPARE(out, "Shaders::DistanceFieldVectorGL::setTextureLayer(): the shader was not created with texture arrays enabled\n"); +} + template void DistanceFieldVectorGLTest::bindTextureTransformBufferNotEnabled() { setTestCaseTemplateName(Utility::format("{}", dimensions)); @@ -1032,36 +1187,89 @@ template void DistanceFieldVectorGLTest::ren !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh square = MeshTools::compile(Primitives::squareSolid(Primitives::SquareFlag::TextureCoordinates)); + struct Vertex { + Vector2 position; + Vector3 textureCoords; + } squareData[] { + {{ 1.0f, -1.0f}, {1.0f, 0.0f, Float(data.layerAttribute)}}, + {{ 1.0f, 1.0f}, {1.0f, 1.0f, Float(data.layerAttribute)}}, + {{-1.0f, -1.0f}, {0.0f, 0.0f, Float(data.layerAttribute)}}, + {{-1.0f, 1.0f}, {0.0f, 1.0f, Float(data.layerAttribute)}} + }; + GL::Mesh square{GL::MeshPrimitive::TriangleStrip}; + #ifndef MAGNUM_TARGET_GLES2 + if(data.arrayTextureCoordinates) { + square.addVertexBuffer(GL::Buffer{squareData}, 0, + GenericGL2D::Position{}, + GenericGL2D::TextureArrayCoordinates{}); + } else + #endif + { + square.addVertexBuffer(GL::Buffer{squareData}, 0, + GenericGL2D::Position{}, + GenericGL2D::TextureCoordinates{}, + sizeof(Float)); + } + square.setCount(4); + + DistanceFieldVectorGL2D::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag & DistanceFieldVectorGL2D::Flag::UniformBuffers && (data.flags & DistanceFieldVectorGL2D::Flag::TextureArrays) && !(data.flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) && data.layerUniform) { + CORRADE_INFO("Texture arrays with layer passed from a uniform currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= DistanceFieldVectorGL2D::Flag::TextureTransformation; + } + #endif + DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Configuration{} + .setFlags(flags)}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D texture; + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge); - - #ifdef MAGNUM_TARGET_GLES2 - /* Don't want to bother with the fiasco of single-channel formats and - texture storage extensions on ES2 */ - texture.setImage(0, TextureFormatR, *image); - #else - texture.setStorage(1, TextureFormatR, image->size()) - .setSubImage(0, {}, *image); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & DistanceFieldVectorGL2D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, {image->size(), data.layerUniform + data.layerAttribute + 1}) + .setSubImage(0, {0, 0, data.layerUniform + data.layerAttribute}, ImageView2D{*image}); + + shader.bindVectorTexture(textureArray); + } else #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif - DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Configuration{} - .setFlags(data.flags|flag)}; - shader.bindVectorTexture(texture); + shader.bindVectorTexture(texture); + } if(flag == DistanceFieldVectorGL2D::Flag{}) { if(data.textureTransformation != Matrix3{}) shader.setTextureMatrix(data.textureTransformation); else shader.setTransformationProjectionMatrix( Matrix3::projection({2.1f, 2.1f})); + #ifndef MAGNUM_TARGET_GLES2 + if(data.layerUniform != 0) /* to verify the default */ + shader.setTextureLayer(data.layerUniform); + #endif shader.setColor(data.color) .setOutlineColor(data.outlineColor) .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) @@ -1095,8 +1303,9 @@ template void DistanceFieldVectorGLTest::ren GL::Buffer textureTransformationlUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layerUniform) }}; - if(data.flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) + if(flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationlUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -1163,30 +1372,79 @@ template void DistanceFieldVectorGLTest::ren !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh plane = MeshTools::compile(Primitives::planeSolid(Primitives::PlaneFlag::TextureCoordinates)); + struct Vertex { + Vector3 position; + Vector3 textureCoords; + } planeData[] { + {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f, Float(data.layerAttribute)}}, + {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, Float(data.layerAttribute)}}, + {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, Float(data.layerAttribute)}}, + {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, Float(data.layerAttribute)}} + }; + GL::Mesh plane{GL::MeshPrimitive::TriangleStrip}; + #ifndef MAGNUM_TARGET_GLES2 + if(data.arrayTextureCoordinates) { + plane.addVertexBuffer(GL::Buffer{planeData}, 0, + GenericGL3D::Position{}, + GenericGL3D::TextureArrayCoordinates{}); + } else + #endif + { + plane.addVertexBuffer(GL::Buffer{planeData}, 0, + GenericGL3D::Position{}, + GenericGL3D::TextureCoordinates{}, + sizeof(Float)); + } + plane.setCount(4); + + DistanceFieldVectorGL3D::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag & DistanceFieldVectorGL3D::Flag::UniformBuffers && (data.flags & DistanceFieldVectorGL3D::Flag::TextureArrays) && !(data.flags & DistanceFieldVectorGL3D::Flag::TextureTransformation) && data.layerUniform) { + CORRADE_INFO("Texture arrays with layer passed from a uniform currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= DistanceFieldVectorGL3D::Flag::TextureTransformation; + } + #endif + DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Configuration{} + .setFlags(flags)}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D texture; + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge); - - #ifdef MAGNUM_TARGET_GLES2 - /* Don't want to bother with the fiasco of single-channel formats and - texture storage extensions on ES2 */ - texture.setImage(0, TextureFormatR, *image); - #else - texture.setStorage(1, TextureFormatR, image->size()) - .setSubImage(0, {}, *image); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & DistanceFieldVectorGL3D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, {image->size(), data.layerUniform + data.layerAttribute + 1}) + .setSubImage(0, {0, 0, data.layerUniform + data.layerAttribute}, ImageView2D{*image}); + + shader.bindVectorTexture(textureArray); + } else #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif - DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Configuration{} - .setFlags(data.flags|flag)}; - shader.bindVectorTexture(texture); + shader.bindVectorTexture(texture); + } if(flag == DistanceFieldVectorGL3D::Flag{}) { if(data.textureTransformation != Matrix3{}) @@ -1196,6 +1454,10 @@ template void DistanceFieldVectorGLTest::ren Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* Matrix4::rotationZ(15.0_degf)); + #ifndef MAGNUM_TARGET_GLES2 + if(data.layerUniform != 0) /* to verify the default */ + shader.setTextureLayer(data.layerUniform); + #endif shader.setColor(data.color) .setOutlineColor(data.outlineColor) .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) @@ -1232,8 +1494,9 @@ template void DistanceFieldVectorGLTest::ren GL::Buffer textureTransformationlUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layerUniform) }}; - if(data.flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) + if(flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationlUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -1308,6 +1571,11 @@ void DistanceFieldVectorGLTest::renderMulti2D() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif + DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Configuration{} + .setFlags(DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation|data.flags) + .setMaterialCount(data.materialCount) + .setDrawCount(data.drawCount)}; + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1317,12 +1585,36 @@ void DistanceFieldVectorGLTest::renderMulti2D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); - GL::Texture2D vector; - vector.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::R8, image->size()) - .setSubImage(0, {}, *image); + + /* For arrays we the original image three times to different offsets in + three different slices */ + GL::Texture2D vector{NoCreate}; + GL::Texture2DArray vectorArray{NoCreate}; + if(data.flags & DistanceFieldVectorGL2D::Flag::TextureArrays) { + Vector3i size{image->size().x(), image->size().y()*2, 6}; + + vectorArray = GL::Texture2DArray{}; + vectorArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, size) + /* Clear to all zeros for reproducible output */ + .setSubImage(0, {}, Image3D{PixelFormat::R8Unorm, size, Containers::Array{ValueInit, std::size_t(size.product())}}) + .setSubImage(0, {0, size.y()/4, 1}, ImageView2D{*image}) + .setSubImage(0, {0, size.y()/2, 3}, ImageView2D{*image}) + .setSubImage(0, {0, 0, 5}, ImageView2D{*image}); + + shader.bindVectorTexture(vectorArray); + } else { + vector = GL::Texture2D{}; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + shader.bindVectorTexture(vector); + } /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32, @@ -1331,7 +1623,47 @@ void DistanceFieldVectorGLTest::renderMulti2D() { Primitives::SquareFlag::TextureCoordinates)); Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3, Primitives::Circle2DFlag::TextureCoordinates)); - GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + + /* Assuming the texture coordinates are the last attribute, add a four-byte + padding after, which we subsequently abuse as the layer index */ + /** @todo clean this up once MeshData (and primitives?) support array + coordinates directly */ + Trade::MeshData meshData = MeshTools::interleave( + MeshTools::concatenate({circleData, squareData, triangleData}), + {Trade::MeshAttributeData{4}}); + CORRADE_COMPARE(meshData.attributeCount(), 2); + CORRADE_COMPARE(meshData.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(meshData.attributeName(1), Trade::MeshAttribute::TextureCoordinates); + /* Manual cast because the real attribute type is Vector2 */ + const Containers::StridedArrayView1D textureCoordinates = Containers::arrayCast(meshData.mutableAttribute(Trade::MeshAttribute::TextureCoordinates)); + + /* The circle will use the last slice, coming from just the attribute + alone */ + for(UnsignedInt i = 0; i != circleData.vertexCount(); ++i) + textureCoordinates[i].z() = 5; + /* The square will use the third slice, coming from both the attribute and + the uniform */ + for(UnsignedInt i = 0; i != squareData.vertexCount(); ++i) + textureCoordinates[circleData.vertexCount() + i].z() = 1; + /* The triangle will use the second slice, coming from just the uniform. + The memory isn't initialized by default however, so set the attribute to + 0. */ + for(UnsignedInt i = 0; i != triangleData.vertexCount(); ++i) + textureCoordinates[circleData.vertexCount() + squareData.vertexCount() + i].z() = 0; + + /* Making some assumptions about the layout for simplicity */ + CORRADE_COMPARE(meshData.attributeStride(0), sizeof(Vector2) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeStride(1), sizeof(Vector2) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeOffset(0), 0); + CORRADE_COMPARE(meshData.attributeOffset(1), sizeof(Vector2)); + GL::Mesh mesh; + mesh.addVertexBuffer(GL::Buffer{meshData.vertexData()}, 0, + GenericGL2D::Position{}, + GenericGL2D::TextureArrayCoordinates{}) + .setIndexBuffer(GL::Buffer{GL::Buffer::TargetHint::ElementArray, meshData.indexData()}, 0, + meshData.indexType()) + .setCount(meshData.indexCount()); + GL::MeshView circle{mesh}; circle.setCount(circleData.indexCount()); GL::MeshView square{mesh}; @@ -1379,17 +1711,30 @@ void DistanceFieldVectorGLTest::renderMulti2D() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & DistanceFieldVectorGL2D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.0f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation({0.5f, 0.5f})* Matrix3::rotation(180.0_degf)* - Matrix3::translation({-0.5f, -0.5f}) - ); + Matrix3::translation({-0.5f, -0.5f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & DistanceFieldVectorGL2D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.5f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation(Vector2::xAxis(1.0f))* - Matrix3::scaling(Vector2::xScale(-1.0f)) - ); + Matrix3::scaling(Vector2::xScale(-1.0f))) + .setLayer(2); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} - .setTextureMatrix(Matrix3{}); + .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & DistanceFieldVectorGL2D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.25f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})) + .setLayer(1); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -1403,12 +1748,6 @@ void DistanceFieldVectorGLTest::renderMulti2D() { .setMaterialId(data.bindWithOffset ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Configuration{} - .setFlags(DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation|data.flags) - .setMaterialCount(data.materialCount) - .setDrawCount(data.drawCount)}; - shader.bindVectorTexture(vector); - /* Rebinding UBOs / SSBOs each time */ if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, @@ -1528,6 +1867,11 @@ void DistanceFieldVectorGLTest::renderMulti3D() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif + DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Configuration{} + .setFlags(DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation|data.flags) + .setMaterialCount(data.materialCount) + .setDrawCount(data.drawCount)}; + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1537,12 +1881,36 @@ void DistanceFieldVectorGLTest::renderMulti3D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); - GL::Texture2D vector; - vector.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::R8, image->size()) - .setSubImage(0, {}, *image); + + /* For arrays we the original image three times to different offsets in + three different slices */ + GL::Texture2D vector{NoCreate}; + GL::Texture2DArray vectorArray{NoCreate}; + if(data.flags & DistanceFieldVectorGL3D::Flag::TextureArrays) { + Vector3i size{image->size().x(), image->size().y()*2, 6}; + + vectorArray = GL::Texture2DArray{}; + vectorArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, size) + /* Clear to all zeros for reproducible output */ + .setSubImage(0, {}, Image3D{PixelFormat::R8Unorm, size, Containers::Array{ValueInit, std::size_t(size.product())}}) + .setSubImage(0, {0, size.y()/4, 1}, ImageView2D{*image}) + .setSubImage(0, {0, size.y()/2, 3}, ImageView2D{*image}) + .setSubImage(0, {0, 0, 5}, ImageView2D{*image}); + + shader.bindVectorTexture(vectorArray); + } else { + vector = GL::Texture2D{}; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + shader.bindVectorTexture(vector); + } Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates); @@ -1551,7 +1919,51 @@ void DistanceFieldVectorGLTest::renderMulti3D() { Primitives::PlaneFlag::TextureCoordinates)); Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f, Primitives::ConeFlag::TextureCoordinates); - GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + + /* Assuming the texture coordinates are the last attribute, add a four-byte + padding after, which we subsequently abuse as the layer index */ + /** @todo clean this up once MeshData (and primitives?) support array + coordinates directly */ + Trade::MeshData meshData = MeshTools::interleave( + MeshTools::concatenate({sphereData, planeData, coneData}), + {Trade::MeshAttributeData{4}}); + CORRADE_COMPARE(meshData.attributeCount(), 3); + CORRADE_COMPARE(meshData.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(meshData.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(meshData.attributeName(2), Trade::MeshAttribute::TextureCoordinates); + /* Manual cast because the real attribute type is Vector2 */ + const Containers::StridedArrayView1D textureCoordinates = Containers::arrayCast(meshData.mutableAttribute(Trade::MeshAttribute::TextureCoordinates)); + + /* The sphere will use the last slice, coming from just the attribute + alone */ + for(UnsignedInt i = 0; i != sphereData.vertexCount(); ++i) + textureCoordinates[i].z() = 5; + /* The plane will use the third slice, coming from both the attribute and + the uniform */ + for(UnsignedInt i = 0; i != planeData.vertexCount(); ++i) + textureCoordinates[sphereData.vertexCount() + i].z() = 1; + /* The cone will use the first slice, coming from just the uniform. The + memory isn't initialized by default however, so set the attribute to + 0. */ + for(UnsignedInt i = 0; i != coneData.vertexCount(); ++i) + textureCoordinates[sphereData.vertexCount() + planeData.vertexCount() + i].z() = 0; + + /* Making some assumptions about the layout for simplicity */ + CORRADE_COMPARE(meshData.attributeStride(0), sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeStride(1), sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeStride(2), sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeOffset(0), 0); + CORRADE_COMPARE(meshData.attributeOffset(1), sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeOffset(2), sizeof(Vector3) + sizeof(Vector3)); + GL::Mesh mesh; + mesh.addVertexBuffer(GL::Buffer{meshData.vertexData()}, 0, + GenericGL3D::Position{}, + GenericGL3D::Normal{}, + GenericGL3D::TextureArrayCoordinates{}) + .setIndexBuffer(GL::Buffer{GL::Buffer::TargetHint::ElementArray, meshData.indexData()}, 0, + meshData.indexType()) + .setCount(meshData.indexCount()); + GL::MeshView sphere{mesh}; sphere.setCount(sphereData.indexCount()); GL::MeshView plane{mesh}; @@ -1604,17 +2016,30 @@ void DistanceFieldVectorGLTest::renderMulti3D() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & DistanceFieldVectorGL3D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.0f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation({0.5f, 0.5f})* Matrix3::rotation(180.0_degf)* - Matrix3::translation({-0.5f, -0.5f}) - ); + Matrix3::translation({-0.5f, -0.5f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & DistanceFieldVectorGL3D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.5f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation(Vector2::xAxis(1.0f))* - Matrix3::scaling(Vector2::xScale(-1.0f)) - ); + Matrix3::scaling(Vector2::xScale(-1.0f))) + .setLayer(2); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} - .setTextureMatrix(Matrix3{}); + .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & DistanceFieldVectorGL3D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.25f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})) + .setLayer(1); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -1628,12 +2053,6 @@ void DistanceFieldVectorGLTest::renderMulti3D() { .setMaterialId(data.bindWithOffset ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Configuration{} - .setFlags(DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation|data.flags) - .setMaterialCount(data.materialCount) - .setDrawCount(data.drawCount)}; - shader.bindVectorTexture(vector); - /* Rebinding UBOs / SSBOs each time */ if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp index c7fa26382..1d6fbd1b9 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp @@ -92,8 +92,8 @@ void DistanceFieldVectorGL_Test::debugFlag() { void DistanceFieldVectorGL_Test::debugFlags() { Containers::String out; - Debug{&out} << DistanceFieldVectorGL3D::Flags{DistanceFieldVectorGL3D::Flag::TextureTransformation|DistanceFieldVectorGL3D::Flag(0xf0)} << DistanceFieldVectorGL3D::Flags{}; - CORRADE_COMPARE(out, "Shaders::DistanceFieldVectorGL::Flag::TextureTransformation|Shaders::DistanceFieldVectorGL::Flag(0xf0) Shaders::DistanceFieldVectorGL::Flags{}\n"); + Debug{&out} << DistanceFieldVectorGL3D::Flags{DistanceFieldVectorGL3D::Flag::TextureTransformation|DistanceFieldVectorGL3D::Flag(0x80)} << DistanceFieldVectorGL3D::Flags{}; + CORRADE_COMPARE(out, "Shaders::DistanceFieldVectorGL::Flag::TextureTransformation|Shaders::DistanceFieldVectorGL::Flag(0x80) Shaders::DistanceFieldVectorGL::Flags{}\n"); } #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 9c4278397..bf7c6ef1f 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -61,6 +61,7 @@ #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/Extensions.h" #include "Magnum/GL/MeshView.h" +#include "Magnum/GL/TextureArray.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/Primitives/Circle.h" @@ -99,9 +100,12 @@ struct VectorGLTest: GL::OpenGLTester { #ifndef MAGNUM_TARGET_GLES2 template void setUniformUniformBuffersEnabled(); template void bindBufferUniformBuffersNotEnabled(); + template void bindTextureInvalid(); + template void bindTextureArrayInvalid(); #endif template void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 + template void setTextureLayerNotArray(); template void bindTextureTransformBufferNotEnabled(); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -166,7 +170,11 @@ constexpr struct { VectorGL2D::Flags flags; } ConstructData[]{ {"", {}}, - {"texture transformation", VectorGL2D::Flag::TextureTransformation} + {"texture transformation", VectorGL2D::Flag::TextureTransformation}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture arrays", VectorGL2D::Flag::TextureArrays}, + {"texture transformation + texture arrays", VectorGL2D::Flag::TextureTransformation|VectorGL2D::Flag::TextureArrays}, + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -178,12 +186,14 @@ constexpr struct { {"classic fallback", {}, 1, 1}, {"", VectorGL2D::Flag::UniformBuffers, 1, 1}, {"texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1, 1}, + {"texture arrays", VectorGL2D::Flag::TextureArrays, 1, 1}, + {"texture transformation + texture arrays", VectorGL2D::Flag::TextureTransformation|VectorGL2D::Flag::TextureArrays, 1, 1}, /* 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|VectorGL2D::Flag::TextureArrays, 15, 42}, #ifndef MAGNUM_TARGET_WEBGL - {"shader storage + multidraw with all the things", VectorGL2D::Flag::ShaderStorageBuffers|VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 0, 0} + {"shader storage + multidraw with all the things", VectorGL2D::Flag::ShaderStorageBuffers|VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation|VectorGL2D::Flag::TextureArrays, 0, 0} #endif }; #endif @@ -207,17 +217,68 @@ const struct { const char* name; VectorGL2D::Flags flags; Matrix3 textureTransformation; + bool arrayTextureCoordinates; + Int layerAttribute, layerUniform; Color4 backgroundColor, color; const char* file2D; const char* file3D; bool flip; } RenderData[] { - {"texture transformation", VectorGL2D::Flag::TextureTransformation, + {"texture transformation", + VectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + false, 0, 0, 0x00000000_rgbaf, 0xffffff_rgbf, + "defaults.tga", "defaults.tga", true}, + {"", + {}, {}, + false, 0, 0, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + #ifndef MAGNUM_TARGET_GLES2 + {"array texture, 2D coordinates, first layer", + VectorGL2D::Flag::TextureArrays, {}, + false, 0, 0, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + {"array texture, 2D coordinates, arbitrary layer from uniform", + VectorGL2D::Flag::TextureArrays, {}, + false, 0, 6, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + {"array texture, 2D coordinates, texture transformation, arbitrary layer from uniform", + VectorGL2D::Flag::TextureArrays|VectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + false, 0, 6, 0x00000000_rgbaf, 0xffffff_rgbf, + "defaults.tga", "defaults.tga", true}, + {"array texture, array coordinates, first layer", + VectorGL2D::Flag::TextureArrays, {}, + true, 0, 0, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + {"array texture, array coordinates, arbitrary layer from attribute", + VectorGL2D::Flag::TextureArrays, {}, + true, 6, 0, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + {"array texture, array coordinates, arbitrary layer from uniform", + VectorGL2D::Flag::TextureArrays, {}, + true, 0, 6, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + {"array texture, array coordinates, arbitrary layer from both", + VectorGL2D::Flag::TextureArrays, {}, + true, 2, 4, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false}, + {"array texture, array coordinates, texture transformation, arbitrary layer from attribute", + VectorGL2D::Flag::TextureArrays|VectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + true, 6, 0, 0x00000000_rgbaf, 0xffffff_rgbf, + "defaults.tga", "defaults.tga", true}, + {"array texture, array coordinates, texture transformation, arbitrary layer from uniform", + VectorGL2D::Flag::TextureArrays|VectorGL2D::Flag::TextureTransformation, Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), - 0x00000000_rgbaf, 0xffffff_rgbf, + true, 0, 6, 0x00000000_rgbaf, 0xffffff_rgbf, "defaults.tga", "defaults.tga", true}, - {"", {}, {}, 0x9999ff_rgbf, 0xffff99_rgbf, - "vector2D.tga", "vector3D.tga", false} + {"array texture, array coordinates, texture transformation, arbitrary layer from both", + VectorGL2D::Flag::TextureArrays|VectorGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + true, 2, 4, 0x00000000_rgbaf, 0xffffff_rgbf, + "defaults.tga", "defaults.tga", true}, + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -235,31 +296,55 @@ constexpr struct { {}, 1, 1, true, 16, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, + {"bind with offset, texture array", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::TextureArrays, 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}, + {"bind with offset, texture array, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::TextureArrays|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, false, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, + {"draw offset, texture array", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::TextureArrays, 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}, + {"draw offset, texture array, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::TextureArrays|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, false, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, + {"multidraw, texture array", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::TextureArrays|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}, + {"multidraw, texture array, shader storage", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::TextureArrays|VectorGL2D::Flag::ShaderStorageBuffers|VectorGL2D::Flag::MultiDraw, 0, 0, false, 1, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, #endif }; #endif @@ -308,10 +393,16 @@ VectorGLTest::VectorGLTest() { &VectorGLTest::setUniformUniformBuffersEnabled<3>, &VectorGLTest::bindBufferUniformBuffersNotEnabled<2>, &VectorGLTest::bindBufferUniformBuffersNotEnabled<3>, + &VectorGLTest::bindTextureInvalid<2>, + &VectorGLTest::bindTextureInvalid<3>, + &VectorGLTest::bindTextureArrayInvalid<2>, + &VectorGLTest::bindTextureArrayInvalid<3>, #endif &VectorGLTest::setTextureMatrixNotEnabled<2>, &VectorGLTest::setTextureMatrixNotEnabled<3>, #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::setTextureLayerNotArray<2>, + &VectorGLTest::setTextureLayerNotArray<3>, &VectorGLTest::bindTextureTransformBufferNotEnabled<2>, &VectorGLTest::bindTextureTransformBufferNotEnabled<3>, #endif @@ -628,11 +719,13 @@ template void VectorGLTest::setUniformUniformBuffersEnab Error redirectError{&out}; shader.setTransformationProjectionMatrix({}) .setTextureMatrix({}) + .setTextureLayer({}) .setBackgroundColor({}) .setColor({}); CORRADE_COMPARE(out, "Shaders::VectorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" "Shaders::VectorGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::VectorGL::setTextureLayer(): the shader was created with uniform buffers enabled\n" "Shaders::VectorGL::setBackgroundColor(): the shader was created with uniform buffers enabled\n" "Shaders::VectorGL::setColor(): the shader was created with uniform buffers enabled\n"); } @@ -667,6 +760,45 @@ template void VectorGLTest::bindBufferUniformBuffersNotE "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::VectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); } + +template void VectorGLTest::bindTextureInvalid() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2D texture; + VectorGL shader{typename VectorGL::Configuration{} + .setFlags(VectorGL::Flag::TextureArrays)}; + + Containers::String out; + Error redirectError{&out}; + shader.bindVectorTexture(texture); + CORRADE_COMPARE(out, "Shaders::VectorGL::bindVectorTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n"); +} + +template void VectorGLTest::bindTextureArrayInvalid() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2DArray texture; + VectorGL shader; + + Containers::String out; + Error redirectError{&out}; + shader.bindVectorTexture(texture); + CORRADE_COMPARE(out, "Shaders::VectorGL::bindVectorTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n"); +} #endif template void VectorGLTest::setTextureMatrixNotEnabled() { @@ -684,6 +816,25 @@ template void VectorGLTest::setTextureMatrixNotEnabled() } #ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::setTextureLayerNotArray() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2D texture; + VectorGL shader; + + Containers::String out; + Error redirectError{&out}; + shader.setTextureLayer(37); + CORRADE_COMPARE(out, "Shaders::VectorGL::setTextureLayer(): the shader was not created with texture arrays enabled\n"); +} + template void VectorGLTest::bindTextureTransformBufferNotEnabled() { setTestCaseTemplateName(Utility::format("{}", dimensions)); @@ -1007,30 +1158,81 @@ template void VectorGLTest::render2D() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh square = MeshTools::compile(Primitives::squareSolid(Primitives::SquareFlag::TextureCoordinates)); + /** @todo clean this up once MeshData (and primitives?) support array + coordinates directly */ + struct Vertex { + Vector2 position; + Vector3 textureCoords; + } squareData[] { + {{ 1.0f, -1.0f}, {1.0f, 0.0f, Float(data.layerAttribute)}}, + {{ 1.0f, 1.0f}, {1.0f, 1.0f, Float(data.layerAttribute)}}, + {{-1.0f, -1.0f}, {0.0f, 0.0f, Float(data.layerAttribute)}}, + {{-1.0f, 1.0f}, {0.0f, 1.0f, Float(data.layerAttribute)}} + }; + GL::Mesh square{GL::MeshPrimitive::TriangleStrip}; + #ifndef MAGNUM_TARGET_GLES2 + if(data.arrayTextureCoordinates) { + square.addVertexBuffer(GL::Buffer{squareData}, 0, + GenericGL2D::Position{}, + GenericGL2D::TextureArrayCoordinates{}); + } else + #endif + { + square.addVertexBuffer(GL::Buffer{squareData}, 0, + GenericGL2D::Position{}, + GenericGL2D::TextureCoordinates{}, + sizeof(Float)); + } + square.setCount(4); + + VectorGL2D::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag & VectorGL2D::Flag::UniformBuffers && (data.flags & VectorGL2D::Flag::TextureArrays) && !(data.flags & VectorGL2D::Flag::TextureTransformation) && data.layerUniform) { + CORRADE_INFO("Texture arrays with layer passed from a uniform currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= VectorGL2D::Flag::TextureTransformation; + } + #endif + VectorGL2D shader{VectorGL2D::Configuration{} + .setFlags(flags)}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D texture; + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge); - - #ifdef MAGNUM_TARGET_GLES2 - /* Don't want to bother with the fiasco of single-channel formats and - texture storage extensions on ES2 */ - texture.setImage(0, TextureFormatR, *image); - #else - texture.setStorage(1, TextureFormatR, image->size()) - .setSubImage(0, {}, *image); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & VectorGL2D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatR, {image->size(), data.layerUniform + data.layerAttribute + 1}) + .setSubImage(0, {0, 0, data.layerUniform + data.layerAttribute}, ImageView2D{*image}); + + shader.bindVectorTexture(textureArray); + } else #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif - VectorGL2D shader{VectorGL2D::Configuration{} - .setFlags(data.flags|flag)}; - shader.bindVectorTexture(texture); + shader.bindVectorTexture(texture); + } if(flag == VectorGL2D::Flag{}) { shader.setBackgroundColor(data.backgroundColor) @@ -1040,6 +1242,10 @@ template void VectorGLTest::render2D() { else shader.setTransformationProjectionMatrix( Matrix3::projection({2.1f, 2.1f})* Matrix3::rotation(5.0_degf)); + #ifndef MAGNUM_TARGET_GLES2 + if(data.layerUniform != 0) /* to verify the default */ + shader.setTextureLayer(data.layerUniform); + #endif shader.draw(square); } #ifndef MAGNUM_TARGET_GLES2 @@ -1063,13 +1269,14 @@ template void VectorGLTest::render2D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layerUniform) }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { VectorMaterialUniform{} .setBackgroundColor(data.backgroundColor) .setColor(data.color) }}; - if(data.flags & VectorGL2D::Flag::TextureTransformation) + if(flags & VectorGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -1135,30 +1342,79 @@ template void VectorGLTest::render3D() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh plane = MeshTools::compile(Primitives::planeSolid(Primitives::PlaneFlag::TextureCoordinates)); + struct Vertex { + Vector3 position; + Vector3 textureCoords; + } planeData[] { + {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f, Float(data.layerAttribute)}}, + {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, Float(data.layerAttribute)}}, + {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, Float(data.layerAttribute)}}, + {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, Float(data.layerAttribute)}} + }; + GL::Mesh plane{GL::MeshPrimitive::TriangleStrip}; + #ifndef MAGNUM_TARGET_GLES2 + if(data.arrayTextureCoordinates) { + plane.addVertexBuffer(GL::Buffer{planeData}, 0, + GenericGL3D::Position{}, + GenericGL3D::TextureArrayCoordinates{}); + } else + #endif + { + plane.addVertexBuffer(GL::Buffer{planeData}, 0, + GenericGL3D::Position{}, + GenericGL3D::TextureCoordinates{}, + sizeof(Float)); + } + plane.setCount(4); + + VectorGL3D::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag & VectorGL3D::Flag::UniformBuffers && (data.flags & VectorGL3D::Flag::TextureArrays) && !(data.flags & VectorGL3D::Flag::TextureTransformation) && data.layerUniform) { + CORRADE_INFO("Texture arrays with layer passed from a uniform currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= VectorGL3D::Flag::TextureTransformation; + } + #endif + VectorGL3D shader{VectorGL3D::Configuration{} + .setFlags(flags)}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D texture; + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge); - - #ifdef MAGNUM_TARGET_GLES2 - /* Don't want to bother with the fiasco of single-channel formats and - texture storage extensions on ES2 */ - texture.setImage(0, TextureFormatR, *image); - #else - texture.setStorage(1, TextureFormatR, image->size()) - .setSubImage(0, {}, *image); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & VectorGL3D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatR, {image->size(), data.layerUniform + data.layerAttribute + 1}) + .setSubImage(0, {0, 0, data.layerUniform + data.layerAttribute}, ImageView2D{*image}); + + shader.bindVectorTexture(textureArray); + } else #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif - VectorGL3D shader{VectorGL3D::Configuration{} - .setFlags(data.flags|flag)}; - shader.bindVectorTexture(texture); + shader.bindVectorTexture(texture); + } if(flag == VectorGL3D::Flag{}) { shader.setBackgroundColor(data.backgroundColor) @@ -1170,6 +1426,10 @@ template void VectorGLTest::render3D() { Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* Matrix4::rotationZ(15.0_degf)); + #ifndef MAGNUM_TARGET_GLES2 + if(data.layerUniform != 0) /* to verify the default */ + shader.setTextureLayer(data.layerUniform); + #endif shader.draw(plane); } #ifndef MAGNUM_TARGET_GLES2 @@ -1195,13 +1455,14 @@ template void VectorGLTest::render3D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layerUniform) }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { VectorMaterialUniform{} .setBackgroundColor(data.backgroundColor) .setColor(data.color) }}; - if(data.flags & VectorGL3D::Flag::TextureTransformation) + if(flags & VectorGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -1275,6 +1536,11 @@ void VectorGLTest::renderMulti2D() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif + VectorGL2D shader{VectorGL2D::Configuration{} + .setFlags(VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation|data.flags) + .setMaterialCount(data.materialCount) + .setDrawCount(data.drawCount)}; + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1284,21 +1550,85 @@ void VectorGLTest::renderMulti2D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector.tga")) && (image = importer->image2D(0))); - GL::Texture2D vector; - vector.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::R8, image->size()) - .setSubImage(0, {}, *image); - /* Circle is a fan, plane is a strip, make it indexed first */ + /* For arrays we the original image three times to different offsets in + three different slices */ + GL::Texture2D vector{NoCreate}; + GL::Texture2DArray vectorArray{NoCreate}; + if(data.flags & VectorGL2D::Flag::TextureArrays) { + Vector3i size{image->size().x(), image->size().y()*2, 6}; + + vectorArray = GL::Texture2DArray{}; + vectorArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, size) + /* Clear to all zeros for reproducible output */ + .setSubImage(0, {}, Image3D{PixelFormat::R8Unorm, size, Containers::Array{ValueInit, std::size_t(size.product())}}) + .setSubImage(0, {0, size.y()/4, 1}, ImageView2D{*image}) + .setSubImage(0, {0, size.y()/2, 3}, ImageView2D{*image}) + .setSubImage(0, {0, 0, 5}, ImageView2D{*image}); + + shader.bindVectorTexture(vectorArray); + } else { + vector = GL::Texture2D{}; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + shader.bindVectorTexture(vector); + } + + /* Circle is a fan, square is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid( Primitives::SquareFlag::TextureCoordinates)); Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3, Primitives::Circle2DFlag::TextureCoordinates)); - GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + + /* Assuming the texture coordinates are the last attribute, add a four-byte + padding after, which we subsequently abuse as the layer index */ + /** @todo clean this up once MeshData (and primitives?) support array + coordinates directly */ + Trade::MeshData meshData = MeshTools::interleave( + MeshTools::concatenate({circleData, squareData, triangleData}), + {Trade::MeshAttributeData{4}}); + CORRADE_COMPARE(meshData.attributeCount(), 2); + CORRADE_COMPARE(meshData.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(meshData.attributeName(1), Trade::MeshAttribute::TextureCoordinates); + /* Manual cast because the real attribute type is Vector2 */ + const Containers::StridedArrayView1D textureCoordinates = Containers::arrayCast(meshData.mutableAttribute(Trade::MeshAttribute::TextureCoordinates)); + + /* The circle will use the last slice, coming from just the attribute + alone */ + for(UnsignedInt i = 0; i != circleData.vertexCount(); ++i) + textureCoordinates[i].z() = 5; + /* The square will use the third slice, coming from both the attribute and + the uniform */ + for(UnsignedInt i = 0; i != squareData.vertexCount(); ++i) + textureCoordinates[circleData.vertexCount() + i].z() = 1; + /* The triangle will use the second slice, coming from just the uniform. + The memory isn't initialized by default however, so set the attribute to + 0. */ + for(UnsignedInt i = 0; i != triangleData.vertexCount(); ++i) + textureCoordinates[circleData.vertexCount() + squareData.vertexCount() + i].z() = 0; + + /* Making some assumptions about the layout for simplicity */ + CORRADE_COMPARE(meshData.attributeStride(0), sizeof(Vector2) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeStride(1), sizeof(Vector2) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeOffset(0), 0); + CORRADE_COMPARE(meshData.attributeOffset(1), sizeof(Vector2)); + GL::Mesh mesh; + mesh.addVertexBuffer(GL::Buffer{meshData.vertexData()}, 0, + GenericGL2D::Position{}, + GenericGL2D::TextureArrayCoordinates{}) + .setIndexBuffer(GL::Buffer{GL::Buffer::TargetHint::ElementArray, meshData.indexData()}, 0, + meshData.indexType()) + .setCount(meshData.indexCount()); + GL::MeshView circle{mesh}; circle.setCount(circleData.indexCount()); GL::MeshView square{mesh}; @@ -1346,17 +1676,30 @@ void VectorGLTest::renderMulti2D() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & VectorGL2D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.0f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation({0.5f, 0.5f})* Matrix3::rotation(180.0_degf)* - Matrix3::translation({-0.5f, -0.5f}) - ); + Matrix3::translation({-0.5f, -0.5f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & VectorGL2D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.5f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation(Vector2::xAxis(1.0f))* - Matrix3::scaling(Vector2::xScale(-1.0f)) - ); + Matrix3::scaling(Vector2::xScale(-1.0f))) + .setLayer(2); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} - .setTextureMatrix(Matrix3{}); + .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & VectorGL2D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.25f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})) + .setLayer(1); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -1370,12 +1713,6 @@ void VectorGLTest::renderMulti2D() { .setMaterialId(data.bindWithOffset ? 0 : 1); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL2D shader{VectorGL2D::Configuration{} - .setFlags(VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation|data.flags) - .setMaterialCount(data.materialCount) - .setDrawCount(data.drawCount)}; - shader.bindVectorTexture(vector); - /* Rebinding UBOs / SSBOs each time */ if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, @@ -1495,6 +1832,11 @@ void VectorGLTest::renderMulti3D() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif + VectorGL3D shader{VectorGL3D::Configuration{} + .setFlags(VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation|data.flags) + .setMaterialCount(data.materialCount) + .setDrawCount(data.drawCount)}; + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1504,12 +1846,36 @@ void VectorGLTest::renderMulti3D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Path::join(_testDir, "TestFiles/vector.tga")) && (image = importer->image2D(0))); - GL::Texture2D vector; - vector.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::R8, image->size()) - .setSubImage(0, {}, *image); + + /* For arrays we the original image three times to different offsets in + three different slices */ + GL::Texture2D vector{NoCreate}; + GL::Texture2DArray vectorArray{NoCreate}; + if(data.flags & VectorGL2D::Flag::TextureArrays) { + Vector3i size{image->size().x(), image->size().y()*2, 6}; + + vectorArray = GL::Texture2DArray{}; + vectorArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, size) + /* Clear to all zeros for reproducible output */ + .setSubImage(0, {}, Image3D{PixelFormat::R8Unorm, size, Containers::Array{ValueInit, std::size_t(size.product())}}) + .setSubImage(0, {0, size.y()/4, 1}, ImageView2D{*image}) + .setSubImage(0, {0, size.y()/2, 3}, ImageView2D{*image}) + .setSubImage(0, {0, 0, 5}, ImageView2D{*image}); + + shader.bindVectorTexture(vectorArray); + } else { + vector = GL::Texture2D{}; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + shader.bindVectorTexture(vector); + } Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates); @@ -1518,7 +1884,51 @@ void VectorGLTest::renderMulti3D() { Primitives::PlaneFlag::TextureCoordinates)); Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f, Primitives::ConeFlag::TextureCoordinates); - GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + + /* Assuming the texture coordinates are the last attribute, add a four-byte + padding after, which we subsequently abuse as the layer index */ + /** @todo clean this up once MeshData (and primitives?) support array + coordinates directly */ + Trade::MeshData meshData = MeshTools::interleave( + MeshTools::concatenate({sphereData, planeData, coneData}), + {Trade::MeshAttributeData{4}}); + CORRADE_COMPARE(meshData.attributeCount(), 3); + CORRADE_COMPARE(meshData.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(meshData.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(meshData.attributeName(2), Trade::MeshAttribute::TextureCoordinates); + /* Manual cast because the real attribute type is Vector2 */ + const Containers::StridedArrayView1D textureCoordinates = Containers::arrayCast(meshData.mutableAttribute(Trade::MeshAttribute::TextureCoordinates)); + + /* The sphere will use the last slice, coming from just the attribute + alone */ + for(UnsignedInt i = 0; i != sphereData.vertexCount(); ++i) + textureCoordinates[i].z() = 5; + /* The plane will use the third slice, coming from both the attribute and + the uniform */ + for(UnsignedInt i = 0; i != planeData.vertexCount(); ++i) + textureCoordinates[sphereData.vertexCount() + i].z() = 1; + /* The cone will use the first slice, coming from just the uniform. The + memory isn't initialized by default however, so set the attribute to + 0. */ + for(UnsignedInt i = 0; i != coneData.vertexCount(); ++i) + textureCoordinates[sphereData.vertexCount() + planeData.vertexCount() + i].z() = 0; + + /* Making some assumptions about the layout for simplicity */ + CORRADE_COMPARE(meshData.attributeStride(0), sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeStride(1), sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeStride(2), sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeOffset(0), 0); + CORRADE_COMPARE(meshData.attributeOffset(1), sizeof(Vector3)); + CORRADE_COMPARE(meshData.attributeOffset(2), sizeof(Vector3) + sizeof(Vector3)); + GL::Mesh mesh; + mesh.addVertexBuffer(GL::Buffer{meshData.vertexData()}, 0, + GenericGL3D::Position{}, + GenericGL3D::Normal{}, + GenericGL3D::TextureArrayCoordinates{}) + .setIndexBuffer(GL::Buffer{GL::Buffer::TargetHint::ElementArray, meshData.indexData()}, 0, + meshData.indexType()) + .setCount(meshData.indexCount()); + GL::MeshView sphere{mesh}; sphere.setCount(sphereData.indexCount()); GL::MeshView plane{mesh}; @@ -1571,17 +1981,30 @@ void VectorGLTest::renderMulti3D() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & VectorGL3D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.0f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation({0.5f, 0.5f})* Matrix3::rotation(180.0_degf)* - Matrix3::translation({-0.5f, -0.5f}) - ); + Matrix3::translation({-0.5f, -0.5f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & VectorGL3D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.5f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})* Matrix3::translation(Vector2::xAxis(1.0f))* - Matrix3::scaling(Vector2::xScale(-1.0f)) - ); + Matrix3::scaling(Vector2::xScale(-1.0f))) + .setLayer(2); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} - .setTextureMatrix(Matrix3{}); + .setTextureMatrix( + /* Additional Y shift + scale in the array slice */ + (data.flags & VectorGL3D::Flag::TextureArrays ? + Matrix3::translation(Vector2::yAxis(0.25f))* + Matrix3::scaling(Vector2::yScale(0.5f)) : Matrix3{})) + .setLayer(1); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -1595,12 +2018,6 @@ void VectorGLTest::renderMulti3D() { .setMaterialId(data.bindWithOffset ? 0 : 1); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL3D shader{VectorGL3D::Configuration{} - .setFlags(VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation|data.flags) - .setMaterialCount(data.materialCount) - .setDrawCount(data.drawCount)}; - shader.bindVectorTexture(vector); - /* Rebinding UBOs / SSBOs each time */ if(data.bindWithOffset) { shader.bindMaterialBuffer(materialUniform, diff --git a/src/Magnum/Shaders/Test/VectorGL_Test.cpp b/src/Magnum/Shaders/Test/VectorGL_Test.cpp index 7f1fb5c77..584db160d 100644 --- a/src/Magnum/Shaders/Test/VectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VectorGL_Test.cpp @@ -92,8 +92,8 @@ void VectorGL_Test::debugFlag() { void VectorGL_Test::debugFlags() { Containers::String out; - Debug{&out} << VectorGL3D::Flags{VectorGL3D::Flag::TextureTransformation|VectorGL3D::Flag(0xf0)} << VectorGL3D::Flags{}; - CORRADE_COMPARE(out, "Shaders::VectorGL::Flag::TextureTransformation|Shaders::VectorGL::Flag(0xf0) Shaders::VectorGL::Flags{}\n"); + Debug{&out} << VectorGL3D::Flags{VectorGL3D::Flag::TextureTransformation|VectorGL3D::Flag(0x80)} << VectorGL3D::Flags{}; + CORRADE_COMPARE(out, "Shaders::VectorGL::Flag::TextureTransformation|Shaders::VectorGL::Flag(0x80) Shaders::VectorGL::Flags{}\n"); } #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 4f5c1e64a..c248de88e 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -42,12 +42,12 @@ #ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 2) +layout(location = 3) #endif uniform lowp vec4 backgroundColor; /* defaults to zero */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 3) +layout(location = 4) #endif uniform lowp vec4 color #ifndef GL_ES @@ -120,11 +120,23 @@ layout(std140 #ifdef EXPLICIT_BINDING layout(binding = 6) #endif -uniform lowp sampler2D vectorTexture; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + vectorTexture; /* Inputs */ -in mediump vec2 interpolatedTextureCoordinates; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #ifdef MULTI_DRAW flat in highp uint drawId; diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index e6277303f..17f807487 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -24,6 +24,10 @@ DEALINGS IN THE SOFTWARE. */ +#if defined(UNIFORM_BUFFERS) && defined(TEXTURE_ARRAYS) && !defined(GL_ES) +#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 @@ -78,6 +82,14 @@ uniform mediump mat3 textureMatrix ; #endif +#ifdef TEXTURE_ARRAYS +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 2) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +uniform highp uint textureLayer; /* defaults to zero */ +#endif + /* Uniform / shader storage buffers */ #else @@ -127,8 +139,9 @@ layout(std140 #ifdef TEXTURE_TRANSFORMATION struct TextureTransformationUniform { highp vec4 rotationScaling; - highp vec4 offsetReservedReserved; - #define textureTransformation_offset offsetReservedReserved.xy + highp vec4 offsetLayerReserved; + #define textureTransformation_offset offsetLayerReserved.xy + #define textureTransformation_layer offsetLayerReserved.z }; layout(std140 @@ -157,11 +170,23 @@ in highp vec4 position; #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = TEXTURECOORDINATES_ATTRIBUTE_LOCATION) #endif -in mediump vec2 textureCoordinates; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + textureCoordinates; /* Outputs */ -out mediump vec2 interpolatedTextureCoordinates; +out mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #ifdef MULTI_DRAW flat out highp uint drawId; @@ -190,6 +215,9 @@ void main() { #endif #ifdef TEXTURE_TRANSFORMATION mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); + #ifdef TEXTURE_ARRAYS + highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer); + #endif #endif #endif @@ -201,11 +229,18 @@ void main() { #error #endif - interpolatedTextureCoordinates = + interpolatedTextureCoordinates.xy = #ifdef TEXTURE_TRANSFORMATION - (textureMatrix*vec3(textureCoordinates, 1.0)).xy + (textureMatrix*vec3(textureCoordinates.xy, 1.0)).xy #else - textureCoordinates + textureCoordinates.xy #endif ; + #ifdef TEXTURE_ARRAYS + interpolatedTextureCoordinates.z = textureCoordinates.z + #if !defined(UNIFORM_BUFFERS) || defined(TEXTURE_TRANSFORMATION) + + float(textureLayer) + #endif + ; + #endif } diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index 8289753c8..4b576ac29 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -45,6 +45,7 @@ #include #include "Magnum/GL/Buffer.h" +#include "Magnum/GL/TextureArray.h" #endif #ifdef MAGNUM_BUILD_STATIC @@ -110,6 +111,10 @@ template typename VectorGL::CompileState Vec #endif } #endif + #ifndef MAGNUM_TARGET_GLES + if(configuration.flags() >= Flag::TextureArrays) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::EXT::texture_array); + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -133,6 +138,9 @@ template typename VectorGL::CompileState Vec GL::Shader vert{version, GL::Shader::Type::Vertex}; vert.addSource(rs.getString("compatibility.glsl"_s)) .addSource(configuration.flags() & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n"_s : ""_s) + #ifndef MAGNUM_TARGET_GLES2 + .addSource(configuration.flags() & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n"_s : ""_s) + #endif .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n"_s : "#define THREE_DIMENSIONS\n"_s); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { @@ -159,7 +167,11 @@ template typename VectorGL::CompileState Vec .submitCompile(); GL::Shader frag{version, GL::Shader::Type::Fragment}; - frag.addSource(rs.getString("compatibility.glsl"_s)); + frag.addSource(rs.getString("compatibility.glsl"_s)) + #ifndef MAGNUM_TARGET_GLES2 + .addSource(configuration.flags() & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n"_s : ""_s) + #endif + ; #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { #ifndef MAGNUM_TARGET_WEBGL @@ -260,6 +272,10 @@ template VectorGL::VectorGL(CompileState&& s _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"_s); if(_flags & Flag::TextureTransformation) _textureMatrixUniform = uniformLocation("textureMatrix"_s); + #ifndef MAGNUM_TARGET_GLES2 + if(_flags & Flag::TextureArrays) + _textureLayerUniform = uniformLocation("textureLayer"_s); + #endif _backgroundColorUniform = uniformLocation("backgroundColor"_s); _colorUniform = uniformLocation("color"_s); } @@ -299,6 +315,7 @@ template VectorGL::VectorGL(CompileState&& s setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); if(_flags & Flag::TextureTransformation) setTextureMatrix(Matrix3{Math::IdentityInit}); + /* Texture layer is zero by default */ /* Background color is zero by default */ setColor(Color4{1.0f}); } @@ -341,6 +358,17 @@ template VectorGL& VectorGL::set return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template VectorGL& VectorGL::setTextureLayer(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::VectorGL::setTextureLayer(): the shader was created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::VectorGL::setTextureLayer(): the shader was not created with texture arrays enabled", *this); + setUniform(_textureLayerUniform, id); + return *this; +} +#endif + template VectorGL& VectorGL::setBackgroundColor(const Color4& color) { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), @@ -472,10 +500,25 @@ template VectorGL& VectorGL::bin #endif template VectorGL& VectorGL::bindVectorTexture(GL::Texture2D& texture) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::VectorGL::bindVectorTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif texture.bind(TextureUnit); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template VectorGL& VectorGL::bindVectorTexture(GL::Texture2DArray& texture) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::VectorGL::bindVectorTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + #endif + texture.bind(TextureUnit); + return *this; +} +#endif + template class MAGNUM_SHADERS_EXPORT VectorGL<2>; template class MAGNUM_SHADERS_EXPORT VectorGL<3>; @@ -502,6 +545,7 @@ Debug& operator<<(Debug& debug, const VectorGLFlag value) { _c(ShaderStorageBuffers) #endif _c(MultiDraw) + _c(TextureArrays) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -524,7 +568,8 @@ Debug& operator<<(Debug& debug, const VectorGLFlags value) { #ifndef MAGNUM_TARGET_WEBGL VectorGLFlag::ShaderStorageBuffers, /* Superset of UniformBuffers */ #endif - VectorGLFlag::UniformBuffers + VectorGLFlag::UniformBuffers, + VectorGLFlag::TextureArrays #endif }); } diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index e1e935a02..3bbf67f96 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -50,7 +50,8 @@ namespace Implementation { #ifndef MAGNUM_TARGET_WEBGL ShaderStorageBuffers = UniformBuffers|(1 << 3), #endif - MultiDraw = UniformBuffers|(1 << 2) + MultiDraw = UniformBuffers|(1 << 2), + TextureArrays = 1 << 4 #endif }; typedef Containers::EnumSet VectorGLFlags; @@ -87,6 +88,12 @@ Common rendering setup: @snippet Shaders-gl.cpp VectorGL-usage2 +If @ref Flag::TextureArrays is enabled, pass a @ref GL::Texture2DArray instance +instead of @ref GL::Texture2D. The layer is taken from the third coordinate of +@ref TextureArrayCoordinates, if used instead of @ref TextureCoordinates, +otherwise layer @cpp 0 @ce is picked. Additionally, the value of +@ref setTextureLayer(), which is @cpp 0 @ce by default, is added to the layer. + @section Shaders-VectorGL-ubo Uniform buffers See @ref shaders-usage-ubo for a high-level overview that applies to all @@ -142,10 +149,26 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @brief 2D texture coordinates * * @ref shaders-generic "Generic attribute", - * @relativeref{Magnum,Vector2}. + * @relativeref{Magnum,Vector2}. Use either this or the + * @ref TextureArrayCoordinates attribute. */ typedef typename GenericGL::TextureCoordinates TextureCoordinates; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief 2D array texture coordinates + * + * @ref shaders-generic "Generic attribute", + * @relativeref{Magnum,Vector3}. Use either this or the + * @ref TextureCoordinates attribute. The third component is used only + * if @ref Flag::TextureArrays is set. + * @requires_gl30 Extension @gl_extension{EXT,texture_array} + * @requires_gles30 Texture arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Texture arrays are not available in WebGL 1.0. + */ + typedef typename GenericGL::TextureArrayCoordinates TextureArrayCoordinates; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -227,7 +250,27 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ - MultiDraw = UniformBuffers|(1 << 2) + MultiDraw = UniformBuffers|(1 << 2), + + /** + * Use 2D texture arrays. Expects that the texture is supplied via + * @ref bindVectorTexture(GL::Texture2DArray&) instead of + * @ref bindVectorTexture(GL::Texture2D&). The layer is taken from + * the third coordinate of @ref TextureArrayCoordinates, if used + * instead of @ref TextureCoordinates, otherwise layer @cpp 0 @ce + * is picked. Additionally, if @ref Flag::UniformBuffers is not + * enabled, the value of @ref setTextureLayer() is added to the + * layer; if @ref Flag::UniformBuffers is enabled and + * @ref Flag::TextureTransformation is enabled as well, the value + * of @ref TextureTransformationUniform::layer is added to the + * layer. + * @requires_gl30 Extension @gl_extension{EXT,texture_array} + * @requires_gles30 Texture arrays are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Texture arrays are not available in WebGL 1.0. + * @m_since_latest + */ + TextureArrays = 1 << 4, #endif }; @@ -421,6 +464,28 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL */ VectorGL& setTextureMatrix(const Matrix3& matrix); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Set texture array layer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::TextureArrays + * enabled. Initial value is @cpp 0 @ce. If a three-component + * @ref TextureArrayCoordinates attribute is used instead of + * @ref TextureCoordinates, this value is added to the layer coming + * from the third component. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::layer and call + * @ref bindTextureTransformationBuffer() instead. + * @requires_gl30 Extension @gl_extension{EXT,texture_array} + * @requires_gles30 Texture arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Texture arrays are not available in WebGL 1.0. + */ + VectorGL& setTextureLayer(UnsignedInt layer); + #endif + /** * @brief Set background color * @return Reference to self (for method chaining) @@ -579,10 +644,32 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @brief Bind a vector texture * @return Reference to self (for method chaining) * + * If @ref Flag::TextureArrays is enabled, use + * @ref bindVectorTexture(GL::Texture2DArray&) instead. * @see @ref Flag::TextureTransformation, @ref setTextureMatrix() */ VectorGL& bindVectorTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind a vector array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::TextureArrays + * enabled. The layer is taken from the third coordinate of + * @ref TextureArrayCoordinates, if used instead of + * @ref TextureCoordinates, otherwise layer @cpp 0 @ce is picked. + * Additionally, if @ref Flag::UniformBuffers is not enabled, the layer + * index is offset with the value set in @ref setTextureLayer(); if + * @ref Flag::UniformBuffers is enabled and + * @ref Flag::TextureTransformation is enabled as well, the layer index + * is offset with @ref TextureTransformationUniform::layer. + * @see @ref Flag::TextureTransformation, @ref setTextureMatrix() + */ + VectorGL& bindVectorTexture(GL::Texture2DArray& texture); + #endif + /** * @} */ @@ -600,8 +687,11 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, - _backgroundColorUniform{2}, - _colorUniform{3}; + #ifndef MAGNUM_TARGET_GLES2 + _textureLayerUniform{2}, + #endif + _backgroundColorUniform{3}, + _colorUniform{4}; #ifndef MAGNUM_TARGET_GLES2 /* Used instead of all other uniforms when Flag::UniformBuffers is set, so it can alias them */