From 95379ace8e81acd19518a87a7b4487b1e4655415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 1 Jun 2021 14:33:18 +0200 Subject: [PATCH] Shaders: support array textures in Flat and Phong. --- doc/changelog.dox | 2 + src/Magnum/Shaders/Flat.frag | 22 +- src/Magnum/Shaders/Flat.vert | 48 +- src/Magnum/Shaders/FlatGL.cpp | 53 +- src/Magnum/Shaders/FlatGL.h | 109 ++- src/Magnum/Shaders/Generic.h | 8 + src/Magnum/Shaders/Phong.frag | 60 +- src/Magnum/Shaders/Phong.vert | 50 +- src/Magnum/Shaders/PhongGL.cpp | 102 ++- src/Magnum/Shaders/PhongGL.h | 202 +++++- src/Magnum/Shaders/Test/FlatGLTest.cpp | 842 ++++++++++++++++++++---- src/Magnum/Shaders/Test/PhongGLTest.cpp | 807 +++++++++++++++++++---- src/Magnum/Shaders/generic.glsl | 2 +- 13 files changed, 1979 insertions(+), 328 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index e2fc0271d..b0b55502b 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -150,6 +150,8 @@ See also: - All builtin shaders now have opt-in support for uniform buffers on desktop, OpenGL ES 3.0+ and WebGL 2.0, including multi-draw functionality for massive driver overhead reduction +- @ref Shaders::FlatGL and @ref Shaders::PhongGL now support texture arrays, + available also in multi-draw and instanced scenarios - Added @ref Shaders::PhongGL::setNormalTextureScale(), consuming the recently added @ref Trade::MaterialAttribute::NormalTextureScale material attribute diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index c63b22d41..6b783be40 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -41,7 +41,7 @@ #ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 2) +layout(location = 3) #endif uniform lowp vec4 color #ifndef GL_ES @@ -51,7 +51,7 @@ uniform lowp vec4 color #ifdef ALPHA_MASK #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 3) +layout(location = 4) #endif uniform lowp float alphaMask #ifndef GL_ES @@ -62,7 +62,7 @@ uniform lowp float alphaMask #ifdef OBJECT_ID #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 4) +layout(location = 5) #endif /* mediump is just 2^10, which might not be enough, this is 2^16 */ uniform highp uint objectId; /* defaults to zero */ @@ -118,13 +118,25 @@ layout(std140 #ifdef EXPLICIT_BINDING layout(binding = 0) #endif -uniform lowp sampler2D textureData; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + textureData; #endif /* Inputs */ #ifdef TEXTURED -in mediump vec2 interpolatedTextureCoordinates; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index 0cf02e95d..71865a578 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(UNIFORM_BUFFERS) && defined(TEXTURE_ARRAYS) && !defined(GL_ES) +#extension GL_ARB_shader_bit_encoding: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -77,6 +81,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 buffers */ #else @@ -108,8 +120,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 @@ -173,13 +186,25 @@ in highp mat4 instancedTransformationMatrix; #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = TEXTURE_OFFSET_ATTRIBUTE_LOCATION) #endif -in mediump vec2 instancedTextureOffset; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + instancedTextureOffset; #endif /* Outputs */ #ifdef TEXTURED -out mediump vec2 interpolatedTextureCoordinates; +out mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #endif #ifdef VERTEX_COLOR @@ -219,6 +244,9 @@ void main() { transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #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 @@ -240,17 +268,25 @@ void main() { #ifdef TEXTURED /* Texture coordinates, if needed */ - interpolatedTextureCoordinates = + interpolatedTextureCoordinates.xy = #ifdef TEXTURE_TRANSFORMATION (textureMatrix*vec3( #ifdef INSTANCED_TEXTURE_OFFSET - instancedTextureOffset + + instancedTextureOffset.xy + #endif textureCoordinates, 1.0)).xy #else textureCoordinates #endif ; + #ifdef TEXTURE_ARRAYS + interpolatedTextureCoordinates.z = float( + #ifdef INSTANCED_TEXTURE_OFFSET + uint(instancedTextureOffset.z) + + #endif + textureLayer + ); + #endif #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 4df4cdffb..c65fdd11d 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -43,6 +43,7 @@ #include #include "Magnum/GL/Buffer.h" +#include "Magnum/GL/TextureArray.h" #endif namespace Magnum { namespace Shaders { @@ -83,6 +84,13 @@ template FlatGL::FlatGL(const Flags flags "Shaders::FlatGL: draw count can't be zero", ); #endif + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags & Flag::TextureArrays) || (flags & Flag::Textured), + "Shaders::FlatGL: texture arrays enabled but the shader is not textured", ); + CORRADE_ASSERT(!(flags & Flag::UniformBuffers) || !(flags & Flag::TextureArrays) || flags >= (Flag::TextureArrays|Flag::TextureTransformation), + "Shaders::FlatGL: texture arrays require texture transformation enabled as well if uniform buffers are used", ); + #endif + #ifndef MAGNUM_TARGET_GLES if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); @@ -98,6 +106,10 @@ template FlatGL::FlatGL(const Flags flags #endif } #endif + #ifndef MAGNUM_TARGET_GLES + if(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 */ @@ -120,6 +132,9 @@ template FlatGL::FlatGL(const Flags flags vert.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") .addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") + #endif .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") #ifndef MAGNUM_TARGET_GLES2 .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") @@ -138,6 +153,9 @@ template FlatGL::FlatGL(const Flags flags vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Flat.vert")); frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") + #endif .addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") #ifndef MAGNUM_TARGET_GLES2 @@ -205,6 +223,10 @@ template FlatGL::FlatGL(const Flags flags _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); if(flags & Flag::TextureTransformation) _textureMatrixUniform = uniformLocation("textureMatrix"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::TextureArrays) + _textureLayerUniform = uniformLocation("textureLayer"); + #endif _colorUniform = uniformLocation("color"); if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); #ifndef MAGNUM_TARGET_GLES2 @@ -240,6 +262,7 @@ template FlatGL::FlatGL(const Flags flags setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); if(flags & Flag::TextureTransformation) setTextureMatrix(Matrix3{Math::IdentityInit}); + /* Texture layer is zero by default */ setColor(Magnum::Color4{1.0f}); if(flags & Flag::AlphaMask) setAlphaMask(0.5f); /* Object ID is zero by default */ @@ -271,6 +294,17 @@ template FlatGL& FlatGL::setText return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template FlatGL& FlatGL::setTextureLayer(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::FlatGL::setTextureLayer(): the shader was created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::FlatGL::setTextureLayer(): the shader was not created with texture arrays enabled", *this); + setUniform(_textureLayerUniform, id); + return *this; +} +#endif + template FlatGL& FlatGL::setColor(const Magnum::Color4& color) { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), @@ -376,10 +410,25 @@ template FlatGL& FlatGL::bindMat template FlatGL& FlatGL::bindTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::Textured, "Shaders::FlatGL::bindTexture(): the shader was not created with texturing enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::FlatGL::bindTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif texture.bind(TextureUnit); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template FlatGL& FlatGL::bindTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags & Flag::Textured, + "Shaders::FlatGL::bindTexture(): the shader was not created with texturing enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::FlatGL::bindTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + texture.bind(TextureUnit); + return *this; +} +#endif + template class MAGNUM_SHADERS_EXPORT FlatGL<2>; template class MAGNUM_SHADERS_EXPORT FlatGL<3>; @@ -404,6 +453,7 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) { #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) _c(MultiDraw) + _c(TextureArrays) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -426,7 +476,8 @@ Debug& operator<<(Debug& debug, const FlatGLFlags value) { FlatGLFlag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 FlatGLFlag::MultiDraw, /* Superset of UniformBuffers */ - FlatGLFlag::UniformBuffers + FlatGLFlag::UniformBuffers, + FlatGLFlag::TextureArrays #endif }); } diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 6e86fe668..5230d5d8d 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -51,7 +51,8 @@ namespace Implementation { InstancedTextureOffset = (1 << 7)|TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 UniformBuffers = 1 << 8, - MultiDraw = UniformBuffers|(1 << 9) + MultiDraw = UniformBuffers|(1 << 9), + TextureArrays = 1 << 10 #endif }; typedef Containers::EnumSet FlatGLFlags; @@ -231,8 +232,9 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @brief (Instanced) texture offset * @m_since{2020,06} * - * @ref shaders-generic "Generic attribute", @ref Magnum::Vector2. Used - * only if @ref Flag::InstancedTextureOffset is set. + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector2. Use + * either this or the @ref TextureOffsetLayer attribute. Used only if + * @ref Flag::InstancedTextureOffset is set. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, * @gl_extension{EXT,instanced_arrays} or @@ -242,6 +244,24 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: */ typedef typename GenericGL::TextureOffset TextureOffset; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief (Instanced) texture offset and layer + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector3, with + * the last component interpreted as an integer. Use either this or the + * @ref TextureOffset attribute. First two components used only if + * @ref Flag::InstancedTextureOffset is set, third component only if + * @ref Flag::TextureArrays is set. + * @requires_gl33 Extension @gl_extension{EXT,texture_array} and + * @gl_extension{ARB,instanced_arrays} + * @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::TextureOffsetLayer TextureOffsetLayer; + #endif + enum: UnsignedInt { /** * Color shader output. Present always, expects three- or @@ -366,6 +386,13 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * specify that only via the uniform @ref setTextureMatrix(). * Implicitly enables @ref Flag::TextureTransformation. See * @ref Shaders-FlatGL-instancing for more information. + * + * If @ref Flag::TextureArrays is set as well, a three-component + * @ref TextureOffsetLayer attribute can be used instead of + * @ref TextureOffset to specify per-instance texture layer, which + * gets added to the uniform layer numbers set by + * @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, * @gl_extension{EXT,instanced_arrays} or @@ -414,7 +441,25 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ - MultiDraw = UniformBuffers|(1 << 9) + MultiDraw = UniformBuffers|(1 << 9), + + /** + * Use 2D texture arrays. Expects that the texture is supplied via + * @ref bindTexture(GL::Texture2DArray&) instead of + * @ref bindTexture(GL::Texture2D&) and the layer is set via + * @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. If + * @ref Flag::InstancedTextureOffset is set as well and a + * three-component @ref TextureOffsetLayer attribute is used + * instead of @ref TextureOffset, the per-instance and uniform + * layer numbers are added together. + * @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 << 10 #endif }; @@ -574,6 +619,29 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: */ FlatGL& 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 + * @ref Flag::InstancedTextureOffset is set and a three-component + * @ref TextureOffsetLayer attribute is used instead of + * @ref TextureOffset, 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. + */ + FlatGL& setTextureLayer(UnsignedInt layer); + #endif + /** * @brief Set color * @return Reference to self (for method chaining) @@ -760,12 +828,34 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::Textured - * enabled. + * enabled. If @ref Flag::TextureArrays is enabled as well, use + * @ref bindTexture(GL::Texture2DArray&) instead. * @see @ref setColor(), @ref Flag::TextureTransformation, * @ref setTextureMatrix() */ FlatGL& bindTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind a color array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both @ref Flag::Textured + * and @ref Flag::TextureArrays enabled. If @ref Flag::UniformBuffers + * is not enabled, the layer is set via @ref setTextureLayer(); if + * @ref Flag::UniformBuffers is enabled, + * @ref Flag::TextureTransformation has to be enabled as well and the + * layer is set via @ref TextureTransformationUniform::layer. + * @see @ref setColor(), @ref Flag::TextureTransformation, + * @ref setTextureLayer() + * @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. + */ + FlatGL& bindTexture(GL::Texture2DArray& texture); + #endif + /** * @} */ @@ -785,10 +875,13 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, - _colorUniform{2}, - _alphaMaskUniform{3}; + #ifndef MAGNUM_TARGET_GLES2 + _textureLayerUniform{2}, + #endif + _colorUniform{3}, + _alphaMaskUniform{4}; #ifndef MAGNUM_TARGET_GLES2 - Int _objectIdUniform{4}; + Int _objectIdUniform{5}; /* Used instead of all other uniforms when Flag::UniformBuffers is set, so it can alias them */ Int _drawOffsetUniform{0}; diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index e15c60def..07408548a 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -462,6 +462,14 @@ struct TextureTransformationUniform { * * Descibes which layer of a texture array to use. Default value is * @cpp 0.5f @ce. + * + * Used only if @ref FlatGL::Flag::TextureArrays / + * @ref PhongGL::Flag::TextureArrays is enabled, ignored otherwise. If + * @ref FlatGL::Flag::InstancedTextureOffset / + * @ref PhongGL::Flag::InstancedTextureOffset is enabled as well, the + * per-instance layer coming from the @ref FlatGL::TextureOffsetLayer / + * @ref PhongGL::TextureOffsetLayer attribute is added to this value. + * @see @ref FlatGL::setTextureLayer(), @ref PhongGL::setTextureLayer() */ UnsignedInt layer; diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 390e01e2f..1a1827068 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -41,7 +41,7 @@ #ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 4) +layout(location = 5) #endif uniform lowp vec4 ambientColor #ifndef GL_ES @@ -55,7 +55,7 @@ uniform lowp vec4 ambientColor #if LIGHT_COUNT #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 5) +layout(location = 6) #endif uniform lowp vec4 diffuseColor #ifndef GL_ES @@ -64,7 +64,7 @@ uniform lowp vec4 diffuseColor ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 6) +layout(location = 7) #endif uniform lowp vec4 specularColor #ifndef GL_ES @@ -73,7 +73,7 @@ uniform lowp vec4 specularColor ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 7) +layout(location = 8) #endif uniform mediump float shininess #ifndef GL_ES @@ -84,7 +84,7 @@ uniform mediump float shininess #ifdef NORMAL_TEXTURE #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 8) +layout(location = 9) #endif uniform mediump float normalTextureScale #ifndef GL_ES @@ -95,7 +95,7 @@ uniform mediump float normalTextureScale #ifdef ALPHA_MASK #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 9) +layout(location = 10) #endif uniform lowp float alphaMask #ifndef GL_ES @@ -106,16 +106,16 @@ uniform lowp float alphaMask #ifdef OBJECT_ID #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 10) +layout(location = 11) #endif /* mediump is just 2^10, which might not be enough, this is 2^16 */ uniform highp uint objectId; /* defaults to zero */ #endif #if LIGHT_COUNT -/* Needs to be last because it uses locations 11 + LIGHT_COUNT to - 11 + 2*LIGHT_COUNT - 1. Location 11 is lightPositions. Also it can't be - specified as 11 + LIGHT_COUNT because that requires ARB_enhanced_layouts. +/* Needs to be last because it uses locations 12 + LIGHT_COUNT to + 12 + 2*LIGHT_COUNT - 1. Location 12 is lightPositions. Also it can't be + specified as 12 + LIGHT_COUNT because that requires ARB_enhanced_layouts. Same for lightSpecularColors and lightRanges below. */ #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = LIGHT_COLORS_LOCATION) /* I fear this will blow up some drivers */ @@ -226,7 +226,13 @@ layout(std140 #ifdef EXPLICIT_BINDING layout(binding = 0) #endif -uniform lowp sampler2D ambientTexture; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + ambientTexture; #endif #if LIGHT_COUNT @@ -234,21 +240,39 @@ uniform lowp sampler2D ambientTexture; #ifdef EXPLICIT_BINDING layout(binding = 1) #endif -uniform lowp sampler2D diffuseTexture; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + diffuseTexture; #endif #ifdef SPECULAR_TEXTURE #ifdef EXPLICIT_BINDING layout(binding = 2) #endif -uniform lowp sampler2D specularTexture; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + specularTexture; #endif #ifdef NORMAL_TEXTURE #ifdef EXPLICIT_BINDING layout(binding = 3) #endif -uniform lowp sampler2D normalTexture; +uniform lowp + #ifndef TEXTURE_ARRAYS + sampler2D + #else + sampler2DArray + #endif + normalTexture; #endif #endif @@ -269,7 +293,13 @@ in highp vec3 cameraDirection; #endif #if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) -in mediump vec2 interpolatedTextureCoordinates; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 318a28480..e31635031 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(UNIFORM_BUFFERS) && defined(TEXTURE_ARRAYS) && !defined(GL_ES) +#extension GL_ARB_shader_bit_encoding: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -87,10 +91,18 @@ uniform mediump mat3 textureMatrix ; #endif +#ifdef TEXTURE_ARRAYS +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 4) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +uniform highp uint textureLayer; /* defaults to zero */ +#endif + #if LIGHT_COUNT /* Needs to be last because it uses locations 11 to 11 + LIGHT_COUNT - 1 */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 11) +layout(location = 12) #endif uniform highp vec4 lightPositions[LIGHT_COUNT] #ifndef GL_ES @@ -149,8 +161,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 @@ -256,13 +269,25 @@ in highp mat3 instancedNormalMatrix; #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = TEXTURE_OFFSET_ATTRIBUTE_LOCATION) #endif -in mediump vec2 instancedTextureOffset; +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + instancedTextureOffset; #endif /* Outputs */ #ifdef TEXTURED -out mediump vec2 interpolatedTextureCoordinates; +out mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; #endif #ifdef VERTEX_COLOR @@ -311,6 +336,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 #if LIGHT_COUNT mediump const uint lightOffset = draws[drawId].draw_lightOffset; @@ -380,17 +408,25 @@ void main() { #ifdef TEXTURED /* Texture coordinates, if needed */ - interpolatedTextureCoordinates = + interpolatedTextureCoordinates.xy = #ifdef TEXTURE_TRANSFORMATION (textureMatrix*vec3( #ifdef INSTANCED_TEXTURE_OFFSET - instancedTextureOffset + + instancedTextureOffset.xy + #endif textureCoordinates, 1.0)).xy #else textureCoordinates #endif ; + #ifdef TEXTURE_ARRAYS + interpolatedTextureCoordinates.z = float( + #ifdef INSTANCED_TEXTURE_OFFSET + uint(instancedTextureOffset.z) + + #endif + textureLayer + ); + #endif #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 377b5443b..30a9eceb9 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -45,6 +45,7 @@ #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/Buffer.h" +#include "Magnum/GL/TextureArray.h" #endif #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" @@ -101,6 +102,13 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount "Shaders::PhongGL: draw count can't be zero", ); #endif + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags & Flag::TextureArrays) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), + "Shaders::PhongGL: texture arrays enabled but the shader is not textured", ); + CORRADE_ASSERT(!(flags & Flag::UniformBuffers) || !(flags & Flag::TextureArrays) || flags >= (Flag::TextureArrays|Flag::TextureTransformation), + "Shaders::PhongGL: texture arrays require texture transformation enabled as well if uniform buffers are used", ); + #endif + #ifndef MAGNUM_TARGET_GLES if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); @@ -116,6 +124,10 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount #endif } #endif + #ifndef MAGNUM_TARGET_GLES + if(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 */ @@ -196,6 +208,9 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount .addSource(flags & Flag::Bitangent ? "#define BITANGENT\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") .addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") + #endif .addSource(Utility::formatString("#define LIGHT_COUNT {}\n", lightCount)) #ifndef MAGNUM_TARGET_GLES2 .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") @@ -223,6 +238,9 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount .addSource(flags & Flag::DiffuseTexture ? "#define DIFFUSE_TEXTURE\n" : "") .addSource(flags & Flag::SpecularTexture ? "#define SPECULAR_TEXTURE\n" : "") .addSource(flags & Flag::NormalTexture ? "#define NORMAL_TEXTURE\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") + #endif .addSource(flags & Flag::Bitangent ? "#define BITANGENT\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") .addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "") @@ -315,6 +333,10 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount _transformationMatrixUniform = uniformLocation("transformationMatrix"); if(flags & Flag::TextureTransformation) _textureMatrixUniform = uniformLocation("textureMatrix"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::TextureArrays) + _textureLayerUniform = uniformLocation("textureLayer"); + #endif _projectionMatrixUniform = uniformLocation("projectionMatrix"); _ambientColorUniform = uniformLocation("ambientColor"); if(lightCount) { @@ -389,6 +411,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount } if(flags & Flag::TextureTransformation) setTextureMatrix(Matrix3{Math::IdentityInit}); + /* Texture layer is zero by default */ if(flags & Flag::AlphaMask) setAlphaMask(0.5f); /* Object ID is zero by default */ } @@ -506,6 +529,17 @@ PhongGL& PhongGL::setTextureMatrix(const Matrix3& matrix) { return *this; } +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::setTextureLayer(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setTextureLayer(): the shader was created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::PhongGL::setTextureLayer(): the shader was not created with texture arrays enabled", *this); + setUniform(_textureLayerUniform, id); + return *this; +} +#endif + PhongGL& PhongGL::setLightPositions(const Containers::ArrayView positions) { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), @@ -768,34 +802,98 @@ PhongGL& PhongGL::bindLightBuffer(GL::Buffer& buffer, const GLintptr offset, con PhongGL& PhongGL::bindAmbientTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::AmbientTexture, "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::PhongGL::bindAmbientTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif texture.bind(AmbientTextureUnit); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::bindAmbientTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags & Flag::AmbientTexture, + "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + texture.bind(AmbientTextureUnit); + return *this; +} +#endif + PhongGL& PhongGL::bindDiffuseTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::DiffuseTexture, "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::PhongGL::bindDiffuseTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif if(_lightCount) texture.bind(DiffuseTextureUnit); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::bindDiffuseTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags & Flag::DiffuseTexture, + "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + if(_lightCount) texture.bind(DiffuseTextureUnit); + return *this; +} +#endif + PhongGL& PhongGL::bindSpecularTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::SpecularTexture, "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::PhongGL::bindSpecularTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif if(_lightCount) texture.bind(SpecularTextureUnit); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::bindSpecularTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags & Flag::SpecularTexture, + "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + if(_lightCount) texture.bind(SpecularTextureUnit); + return *this; +} +#endif + PhongGL& PhongGL::bindNormalTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::NormalTexture, "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::PhongGL::bindNormalTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif + if(_lightCount) texture.bind(NormalTextureUnit); + return *this; +} + +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::bindNormalTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags & Flag::NormalTexture, + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); if(_lightCount) texture.bind(NormalTextureUnit); return *this; } +#endif PhongGL& PhongGL::bindTextures(GL::Texture2D* ambient, GL::Texture2D* diffuse, GL::Texture2D* specular, GL::Texture2D* normal) { CORRADE_ASSERT(_flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture), "Shaders::PhongGL::bindTextures(): the shader was not created with any textures enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::PhongGL::bindTextures(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif GL::AbstractTexture::bind(AmbientTextureUnit, {ambient, diffuse, specular, normal}); return *this; } @@ -823,6 +921,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) _c(MultiDraw) + _c(TextureArrays) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -849,7 +948,8 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { PhongGL::Flag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */ - PhongGL::Flag::UniformBuffers + PhongGL::Flag::UniformBuffers, + PhongGL::Flag::TextureArrays #endif }); } diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index a16d5be33..c04ef613e 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -396,6 +396,24 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { */ typedef typename GenericGL3D::TextureOffset TextureOffset; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief (Instanced) texture offset and layer + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector3, with + * the last component interpreted as an integer. Use either this or the + * @ref TextureOffset attribute. First two components used only if + * @ref Flag::InstancedTextureOffset is set, third component only if + * @ref Flag::TextureArrays is set. + * @requires_gl33 Extension @gl_extension{EXT,texture_array} and + * @gl_extension{ARB,instanced_arrays} + * @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 GenericGL3D::TextureOffsetLayer TextureOffsetLayer; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -553,6 +571,13 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * specify that only via the uniform @ref setTextureMatrix(). * Implicitly enables @ref Flag::TextureTransformation. See * @ref Shaders-PhongGL-instancing for more information. + * + * If @ref Flag::TextureArrays is set as well, a three-component + * @ref TextureOffsetLayer attribute can be used instead of + * @ref TextureOffset to specify per-instance texture layer, which + * gets added to the uniform layer numbers set by + * @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, * @gl_extension{EXT,instanced_arrays} or @@ -602,7 +627,31 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ - MultiDraw = UniformBuffers|(1 << 13) + MultiDraw = UniformBuffers|(1 << 13), + + /** + * Use 2D texture arrays. Expects that the texture is supplied via + * @ref bindAmbientTexture(GL::Texture2DArray&) / + * @ref bindDiffuseTexture(GL::Texture2DArray&) / + * @ref bindSpecularTexture(GL::Texture2DArray&) / + * @ref bindNormalTexture(GL::Texture2DArray&) instead of + * @ref bindAmbientTexture(GL::Texture2D&) / + * @ref bindDiffuseTexture(GL::Texture2D&) / + * @ref bindSpecularTexture(GL::Texture2D&) / + * @ref bindNormalTexture(GL::Texture2D&) and the layer shared by + * all textures is set via @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. If + * @ref Flag::InstancedTextureOffset is set as well and a + * three-component @ref TextureOffsetLayer attribute is used + * instead of @ref TextureOffset, the per-instance and uniform + * layer numbers are added together. + * @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 << 14 #endif }; @@ -934,6 +983,29 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { */ PhongGL& 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 + * @ref Flag::InstancedTextureOffset is set and a three-component + * @ref TextureOffsetLayer attribute is used instead of + * @ref TextureOffset, 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. + */ + PhongGL& setTextureLayer(UnsignedInt layer); + #endif + /** * @brief Set light positions * @return Reference to self (for method chaining) @@ -1335,34 +1407,103 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::AmbientTexture - * enabled. + * enabled. If @ref Flag::TextureArrays is enabled as well, use + * @ref bindAmbientTexture(GL::Texture2DArray&) instead. * @see @ref bindTextures(), @ref setAmbientColor(), * @ref Shaders-PhongGL-lights-ambient */ PhongGL& bindAmbientTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind an ambient array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both + * @ref Flag::AmbientTexture and @ref Flag::TextureArrays enabled. If + * @ref Flag::UniformBuffers is not enabled, the layer is set via + * @ref setTextureLayer(); if @ref Flag::UniformBuffers is enabled, + * @ref Flag::TextureTransformation has to be enabled as well and the + * layer is set via @ref TextureTransformationUniform::layer. + * @see @ref setAmbientColor(), @ref Shaders-PhongGL-lights-ambient + * @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. + */ + PhongGL& bindAmbientTexture(GL::Texture2DArray& texture); + #endif + /** * @brief Bind a diffuse texture * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::DiffuseTexture - * enabled. If @ref lightCount() is zero, this function is a no-op, as - * diffuse color doesn't contribute to the output in that case. + * enabled. If @ref Flag::TextureArrays is enabled as well, use + * @ref bindDiffuseTexture(GL::Texture2DArray&) instead. If + * @ref lightCount() is zero, this function is a no-op, as diffuse + * color doesn't contribute to the output in that case. * @see @ref bindTextures(), @ref setDiffuseColor() */ PhongGL& bindDiffuseTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind a diffuse array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both + * @ref Flag::DiffuseTexture and @ref Flag::TextureArrays + * enabled. If @ref Flag::UniformBuffers is not enabled, the layer is + * set via @ref setTextureLayer(); if @ref Flag::UniformBuffers is + * enabled, @ref Flag::TextureTransformation has to be enabled as well + * and the layer is set via @ref TextureTransformationUniform::layer. + * If @ref lightCount() is zero, this function is a no-op, as diffuse + * color doesn't contribute to the output in that case. + * @see @ref setDiffuseColor() + * @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. + */ + PhongGL& bindDiffuseTexture(GL::Texture2DArray& texture); + #endif + /** * @brief Bind a specular texture * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::SpecularTexture - * enabled. If @ref lightCount() is zero, this function is a no-op, as - * specular color doesn't contribute to the output in that case. + * enabled. If @ref Flag::TextureArrays is enabled as well, use + * @ref bindSpecularTexture(GL::Texture2DArray&) instead. If + * @ref lightCount() is zero, this function is a no-op, as specular + * color doesn't contribute to the output in that case. * @see @ref bindTextures(), @ref setSpecularColor() */ PhongGL& bindSpecularTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind a specular array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both + * @ref Flag::SpecularTexture and @ref Flag::TextureArrays enabled. If + * @ref Flag::UniformBuffers is not enabled, the layer is set via + * @ref setTextureLayer(); if @ref Flag::UniformBuffers is enabled, + * @ref Flag::TextureTransformation has to be enabled as well and the + * layer is set via @ref TextureTransformationUniform::layer. If + * @ref lightCount() is zero, this function is a no-op, as specular + * color doesn't contribute to the output in that case. + * @see @ref setSpecularColor() + * @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. + */ + PhongGL& bindSpecularTexture(GL::Texture2DArray& texture); + #endif + /** * @brief Bind a normal texture * @return Reference to self (for method chaining) @@ -1370,6 +1511,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * Expects that the shader was created with @ref Flag::NormalTexture * enabled and the @ref Tangent attribute was supplied. If + * @ref Flag::TextureArrays is enabled as well, use + * @ref bindNormalTexture(GL::Texture2DArray&) instead. If * @ref lightCount() is zero, this function is a no-op, as normals * don't contribute to the output in that case. * @see @ref Shaders-PhongGL-normal-mapping, @@ -1377,6 +1520,23 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { */ PhongGL& bindNormalTexture(GL::Texture2D& texture); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind a normal array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both + * @ref Flag::NormalTexture and @ref Flag::TextureArrays enabled and + * the @ref Tangent attribute was supplied. If @ref lightCount() is + * zero, this function is a no-op, as normals don't contribute to the + * output in that case. + * @see @ref Shaders-PhongGL-normal-mapping, + * @ref setNormalTextureScale() + */ + PhongGL& bindNormalTexture(GL::Texture2DArray& texture); + #endif + /** * @brief Bind textures * @return Reference to self (for method chaining) @@ -1385,8 +1545,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @ref PhongGL::Flag "Flag" is set, you can use @cpp nullptr @ce for * the rest. Expects that the shader was created with at least one of * @ref Flag::AmbientTexture, @ref Flag::DiffuseTexture, - * @ref Flag::SpecularTexture or @ref Flag::NormalTexture enabled. More - * efficient than setting each texture separately. + * @ref Flag::SpecularTexture or @ref Flag::NormalTexture enabled and + * @ref Flag::TextureArrays is not set. More efficient than setting + * each texture separately. * @see @ref bindAmbientTexture(), @ref bindDiffuseTexture(), * @ref bindSpecularTexture(), @ref bindNormalTexture() */ @@ -1418,19 +1579,22 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { _projectionMatrixUniform{1}, _normalMatrixUniform{2}, _textureMatrixUniform{3}, - _ambientColorUniform{4}, - _diffuseColorUniform{5}, - _specularColorUniform{6}, - _shininessUniform{7}, - _normalTextureScaleUniform{8}, - _alphaMaskUniform{9}; + #ifndef MAGNUM_TARGET_GLES2 + _textureLayerUniform{4}, + #endif + _ambientColorUniform{5}, + _diffuseColorUniform{6}, + _specularColorUniform{7}, + _shininessUniform{8}, + _normalTextureScaleUniform{9}, + _alphaMaskUniform{10}; #ifndef MAGNUM_TARGET_GLES2 - Int _objectIdUniform{10}; + Int _objectIdUniform{11}; #endif - Int _lightPositionsUniform{11}, - _lightColorsUniform, /* 11 + lightCount, set in the constructor */ - _lightSpecularColorsUniform, /* 11 + 2*lightCount */ - _lightRangesUniform; /* 11 + 3*lightCount */ + Int _lightPositionsUniform{12}, + _lightColorsUniform, /* 12 + lightCount, set in the constructor */ + _lightSpecularColorsUniform, /* 12 + 2*lightCount */ + _lightRangesUniform; /* 12 + 3*lightCount */ #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/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 5a50d4e87..cfb25c0ee 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -58,6 +58,7 @@ #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/MeshView.h" +#include "Magnum/GL/TextureArray.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/Primitives/Cone.h" @@ -84,7 +85,7 @@ struct FlatGLTest: GL::OpenGLTester { template void constructMoveUniformBuffers(); #endif - template void constructTextureTransformationNotTextured(); + template void constructInvalid(); #ifndef MAGNUM_TARGET_GLES2 template void constructUniformBuffersInvalid(); #endif @@ -93,10 +94,14 @@ struct FlatGLTest: GL::OpenGLTester { template void setUniformUniformBuffersEnabled(); template void bindBufferUniformBuffersNotEnabled(); #endif - template void bindTextureNotEnabled(); + template void bindTextureInvalid(); + #ifndef MAGNUM_TARGET_GLES2 + template void bindTextureArrayInvalid(); + #endif template void setAlphaMaskNotEnabled(); template void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 + template void setTextureLayerNotArray(); template void bindTextureTransformBufferNotEnabled(); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -163,19 +168,20 @@ struct FlatGLTest: GL::OpenGLTester { [I] instancing [O] UBOs + draw offset [M] multidraw + [L] texture arrays - Mesa Intel BADIOM - ES2 xx + Mesa Intel BADIOML + ES2 xxx ES3 BAD Ox Mesa AMD BAD Mesa llvmpipe BAD - SwiftShader ES2 BAD xx + SwiftShader ES2 BAD xxx ES3 BAD - ANGLE ES2 xx + ANGLE ES2 xxx ES3 BAD OM - ARM Mali (Huawei P10) ES2 BAD xx + ARM Mali (Huawei P10) ES2 BAD xxx ES3 BAD Ox - WebGL (on Mesa Intel) 1.0 BAD xx + WebGL (on Mesa Intel) 1.0 BAD xxx 2.0 BAD OM NVidia BAD Intel Windows BAD @@ -193,6 +199,10 @@ constexpr struct { {"", {}}, {"textured", FlatGL2D::Flag::Textured}, {"textured + texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture arrays", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays}, + {"texture arrays + texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::TextureTransformation}, + #endif {"alpha mask", FlatGL2D::Flag::AlphaMask}, {"alpha mask + textured", FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::Textured}, {"vertex colors", FlatGL2D::Flag::VertexColor}, @@ -203,7 +213,10 @@ constexpr struct { {"object ID + alpha mask + textured", FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::Textured}, #endif {"instanced transformation", FlatGL2D::Flag::InstancedTransformation}, - {"instanced texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTextureOffset} + {"instanced texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTextureOffset}, + #ifndef MAGNUM_TARGET_GLES2 + {"instanced texture array offset + layer", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::InstancedTextureOffset}, + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -219,12 +232,27 @@ constexpr struct { {"multiple materials, draws", FlatGL2D::Flag::UniformBuffers, 8, 48}, {"textured", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured, 1, 1}, {"textured + texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, + {"texture arrays + texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, {"alpha mask", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::AlphaMask, 1, 1}, {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 1, 1}, - {"multidraw with all the things", FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId, 8, 48} + {"instanced texture array offset + layer", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::InstancedTextureOffset, 1, 1}, + {"multidraw with all the things", FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::AlphaMask|FlatGL2D::Flag::ObjectId|FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId, 8, 48} }; #endif +constexpr struct { + const char* name; + FlatGL2D::Flags flags; + const char* message; +} ConstructInvalidData[]{ + {"texture transformation but not textured", FlatGL2D::Flag::TextureTransformation, + "texture transformation enabled but the shader is not textured"}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture arrays but not textured", FlatGL2D::Flag::TextureArrays, + "texture arrays enabled but the shader is not textured"} + #endif +}; + #ifndef MAGNUM_TARGET_GLES2 constexpr struct { const char* name; @@ -236,20 +264,75 @@ constexpr struct { "draw count can't be zero"}, {"zero materials", FlatGL2D::Flag::UniformBuffers, 0, 1, "material count can't be zero"}, + {"texture arrays but no transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, 1, 1, + "texture arrays require texture transformation enabled as well if uniform buffers are used"} +}; +#endif + +constexpr struct { + const char* name; + FlatGL2D::Flags flags; + const char* message; +} BindTextureInvalidData[]{ + {"not textured", {}, + "the shader was not created with texturing enabled"}, + #ifndef MAGNUM_TARGET_GLES2 + {"array", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + "the shader was created with texture arrays enabled, use a Texture2DArray instead"} + #endif +}; + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + FlatGL2D::Flags flags; + const char* message; +} BindTextureArrayInvalidData[]{ + {"not textured", {}, + "the shader was not created with texturing enabled"}, + {"not array", FlatGL2D::Flag::Textured, + "the shader was not created with texture arrays enabled, use a Texture2D instead"} }; #endif +const struct { + const char* name; + FlatGL2D::Flags flags; + Int layer; +} RenderSinglePixelTexturedData[]{ + {"", {}, 0}, + #ifndef MAGNUM_TARGET_GLES2 + {"array, first layer", FlatGL2D::Flag::TextureArrays, 0}, + {"array, arbitrary layer", FlatGL2D::Flag::TextureArrays, 6}, + #endif +}; + const struct { const char* name; FlatGL2D::Flags flags; Matrix3 textureTransformation; + Int layer; bool flip; } RenderTexturedData[]{ - {"", FlatGL2D::Flag::Textured, {}, false}, + {"", + FlatGL2D::Flag::Textured, + {}, 0, false}, {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), - true}, + 0, true}, + #ifndef MAGNUM_TARGET_GLES2 + {"array, first layer", + FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + {}, 0, false}, + {"array, arbitrary layer", + FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + {}, 6, false}, + {"array, texture transformation, arbitrary layer", + FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + 6, true}, + #endif }; const struct { @@ -272,6 +355,7 @@ const struct { FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 0.5f}, {"masking 1.0", "TestFiles/alpha-mask1.0.tga", "TestFiles/alpha-mask1.0.tga", false, FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1.0f} + /* texture arrays are orthogonal to this, no need to be tested here */ }; constexpr struct { @@ -289,6 +373,14 @@ constexpr struct { FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::Textured, /* Minor differences on SwiftShader */ 192.67f, 0.140f}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture array", "instanced-textured2D.tga", "instanced-textured3D.tga", + FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around); minor differences on + SwiftShader */ + 192.67f, 0.398f}, + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -308,6 +400,12 @@ constexpr struct { 1, 1, 16, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, + {"bind with offset, texture array", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 1, 1, 16, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 65.0f, 0.15f}, {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", {}, 2, 3, 1, 0.0f, 0.0f}, @@ -316,13 +414,25 @@ constexpr struct { 2, 3, 1, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, + {"draw offset, texture array", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 2, 3, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 65.0f, 0.15f}, {"multidraw, colored", "multidraw2D.tga", "multidraw3D.tga", FlatGL2D::Flag::MultiDraw, 2, 3, 1, 0.0f, 0.0f}, {"multidraw, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, 2, 3, 1, /* Minor differences on ARM Mali */ - 2.34f, 0.01f} + 2.34f, 0.01f}, + {"multidraw, texture array", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, + 2, 3, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 65.0f, 0.15f} }; #endif @@ -347,9 +457,12 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::constructMoveUniformBuffers<2>, &FlatGLTest::constructMoveUniformBuffers<3>, #endif + }); - &FlatGLTest::constructTextureTransformationNotTextured<2>, - &FlatGLTest::constructTextureTransformationNotTextured<3>}); + addInstancedTests({ + &FlatGLTest::constructInvalid<2>, + &FlatGLTest::constructInvalid<3>}, + Containers::arraySize(ConstructInvalidData)); #ifndef MAGNUM_TARGET_GLES2 addInstancedTests({ @@ -365,13 +478,30 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::bindBufferUniformBuffersNotEnabled<2>, &FlatGLTest::bindBufferUniformBuffersNotEnabled<3>, #endif - &FlatGLTest::bindTextureNotEnabled<2>, - &FlatGLTest::bindTextureNotEnabled<3>, + }); + + addInstancedTests({ + &FlatGLTest::bindTextureInvalid<2>, + &FlatGLTest::bindTextureInvalid<3>}, + Containers::arraySize(BindTextureInvalidData)); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &FlatGLTest::bindTextureArrayInvalid<2>, + &FlatGLTest::bindTextureArrayInvalid<3>}, + Containers::arraySize(BindTextureArrayInvalidData)); + #endif + + addTests({ &FlatGLTest::setAlphaMaskNotEnabled<2>, &FlatGLTest::setAlphaMaskNotEnabled<3>, &FlatGLTest::setTextureMatrixNotEnabled<2>, &FlatGLTest::setTextureMatrixNotEnabled<3>, #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::setTextureLayerNotArray<2>, + &FlatGLTest::setTextureLayerNotArray<3>, + #endif + #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::bindTextureTransformBufferNotEnabled<2>, &FlatGLTest::bindTextureTransformBufferNotEnabled<3>, #endif @@ -403,6 +533,12 @@ FlatGLTest::FlatGLTest() { #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderColored3D, #endif + }, + &FlatGLTest::renderSetup, + &FlatGLTest::renderTeardown); + + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &FlatGLTest::renderSinglePixelTextured2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderSinglePixelTextured2D, @@ -412,6 +548,7 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderSinglePixelTextured3D #endif }, + Containers::arraySize(RenderSinglePixelTexturedData), &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); @@ -540,6 +677,8 @@ template void FlatGLTest::construct() { #ifndef MAGNUM_TARGET_GLES if((data.flags & FlatGL2D::Flag::ObjectId) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif FlatGL shader{data.flags}; @@ -567,6 +706,8 @@ template void FlatGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); if((data.flags & FlatGL2D::Flag::ObjectId) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif if(data.flags >= FlatGL2D::Flag::MultiDraw) { @@ -651,8 +792,10 @@ template void FlatGLTest::constructMoveUniformBuffers() } #endif -template void FlatGLTest::constructTextureTransformationNotTextured() { +template void FlatGLTest::constructInvalid() { + auto&& data = ConstructInvalidData[testCaseInstanceId()]; setTestCaseTemplateName(std::to_string(dimensions)); + setTestCaseDescription(data.name); #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -660,9 +803,9 @@ template void FlatGLTest::constructTextureTransformation std::ostringstream out; Error redirectError{&out}; - FlatGL{FlatGL::Flag::TextureTransformation}; - CORRADE_COMPARE(out.str(), - "Shaders::FlatGL: texture transformation enabled but the shader is not textured\n"); + FlatGL{data.flags}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::FlatGL: {}\n", data.message)); } #ifndef MAGNUM_TARGET_GLES2 @@ -707,12 +850,14 @@ template void FlatGLTest::setUniformUniformBuffersEnable FlatGL shader{FlatGL::Flag::UniformBuffers}; shader.setTransformationProjectionMatrix({}) .setTextureMatrix({}) + .setTextureLayer({}) .setColor({}) .setAlphaMask({}) .setObjectId({}); CORRADE_COMPARE(out.str(), "Shaders::FlatGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" "Shaders::FlatGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::FlatGL::setTextureLayer(): the shader was created with uniform buffers enabled\n" "Shaders::FlatGL::setColor(): the shader was created with uniform buffers enabled\n" "Shaders::FlatGL::setAlphaMask(): the shader was created with uniform buffers enabled\n" "Shaders::FlatGL::setObjectId(): the shader was created with uniform buffers enabled\n"); @@ -752,22 +897,55 @@ template void FlatGLTest::bindBufferUniformBuffersNotEna } #endif -template void FlatGLTest::bindTextureNotEnabled() { +template void FlatGLTest::bindTextureInvalid() { + auto&& data = BindTextureInvalidData[testCaseInstanceId()]; setTestCaseTemplateName(std::to_string(dimensions)); + setTestCaseDescription(data.name); #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + std::ostringstream out; Error redirectError{&out}; + FlatGL shader{data.flags}; GL::Texture2D texture; - FlatGL shader; shader.bindTexture(texture); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::FlatGL::bindTexture(): {}\n", data.message)); +} - CORRADE_COMPARE(out.str(), "Shaders::FlatGL::bindTexture(): the shader was not created with texturing enabled\n"); +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::bindTextureArrayInvalid() { + auto&& data = BindTextureArrayInvalidData[testCaseInstanceId()]; + setTestCaseTemplateName(std::to_string(dimensions)); + setTestCaseDescription(data.name); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + FlatGL shader{data.flags}; + GL::Texture2DArray textureArray; + shader.bindTexture(textureArray); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::FlatGL::bindTexture(): {}\n", data.message)); } +#endif template void FlatGLTest::setAlphaMaskNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -803,6 +981,25 @@ template void FlatGLTest::setTextureMatrixNotEnabled() { "Shaders::FlatGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::setTextureLayerNotArray() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + FlatGL shader; + shader.setTextureLayer(37); + + CORRADE_COMPARE(out.str(), + "Shaders::FlatGL::setTextureLayer(): the shader was not created with texture arrays enabled\n"); +} +#endif + #ifndef MAGNUM_TARGET_GLES2 template void FlatGLTest::bindTextureTransformBufferNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -1155,6 +1352,9 @@ constexpr GL::TextureFormat TextureFormatRGBA = ; template void FlatGLTest::renderSinglePixelTextured2D() { + auto&& data = RenderSinglePixelTexturedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1166,20 +1366,50 @@ template void FlatGLTest::renderSinglePixelTextured2D() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates)); - const Color4ub diffuseData[]{ 0x9999ff_rgb }; - ImageView2D diffuseImage{PixelFormat::RGBA8Unorm, Vector2i{1}, diffuseData}; - GL::Texture2D texture; - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGBA, Vector2i{1}) - .setSubImage(0, {}, diffuseImage); + FlatGL2D::Flags flags = FlatGL2D::Flag::Textured|data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= FlatGL2D::Flag::TextureTransformation; + } + #endif + FlatGL2D shader{flags}; - FlatGL2D shader{FlatGL2D::Flag::Textured|flag}; - shader.bindTexture(texture); + const Color4ub imageData[]{ 0x9999ff_rgb }; + ImageView2D image{PixelFormat::RGBA8Unorm, Vector2i{1}, imageData}; + + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags & FlatGL3D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, image); + shader.bindTexture(textureArray); + if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, image); + shader.bindTexture(texture); + } if(flag == FlatGL2D::Flag{}) { shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) @@ -1194,9 +1424,17 @@ template void FlatGLTest::renderSinglePixelTextured2D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setLayer(data.layer) + }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} }}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & FlatGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) @@ -1226,6 +1464,9 @@ template void FlatGLTest::renderSinglePixelTextured2D() { } template void FlatGLTest::renderSinglePixelTextured3D() { + auto&& data = RenderSinglePixelTexturedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1237,20 +1478,50 @@ template void FlatGLTest::renderSinglePixelTextured3D() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); - const Color4ub diffuseData[]{ 0x9999ff_rgb }; - ImageView2D diffuseImage{PixelFormat::RGBA8Unorm, Vector2i{1}, diffuseData}; - GL::Texture2D texture; - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGBA, Vector2i{1}) - .setSubImage(0, {}, diffuseImage); + FlatGL3D::Flags flags = FlatGL2D::Flag::Textured|data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= FlatGL3D::Flag::TextureTransformation; + } + #endif + FlatGL3D shader{flags}; - FlatGL3D shader{FlatGL3D::Flag::Textured|flag}; - shader.bindTexture(texture); + const Color4ub imageData[]{ 0x9999ff_rgb }; + ImageView2D image{PixelFormat::RGBA8Unorm, Vector2i{1}, imageData}; + + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags & FlatGL3D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, image); + shader.bindTexture(textureArray); + if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, image); + shader.bindTexture(texture); + } if(flag == FlatGL3D::Flag{}) { shader.setTransformationProjectionMatrix( @@ -1275,9 +1546,17 @@ template void FlatGLTest::renderSinglePixelTextured3D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setLayer(data.layer) + }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} }}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & FlatGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) @@ -1321,6 +1600,11 @@ template void FlatGLTest::renderTextured2D() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1331,17 +1615,42 @@ template void FlatGLTest::renderTextured2D() { Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D texture; Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - FlatGL2D shader{data.flags|flag}; - shader.bindTexture(texture); + FlatGL2D::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL2D::Flag::TextureArrays) && !(data.flags & FlatGL2D::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= FlatGL2D::Flag::TextureTransformation; + } + #endif + FlatGL2D shader{flags}; + + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags & FlatGL3D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); + shader.bindTexture(textureArray); + if(flag != FlatGL2D::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindTexture(texture); + } if(flag == FlatGL2D::Flag{}) { if(data.textureTransformation != Matrix3{}) @@ -1364,12 +1673,15 @@ template void FlatGLTest::renderTextured2D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layer) }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} .setColor(0x9999ff_rgbf) }}; - if(data.flags & FlatGL2D::Flag::TextureTransformation) + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & FlatGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -1414,6 +1726,11 @@ template void FlatGLTest::renderTextured3D() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1424,17 +1741,42 @@ template void FlatGLTest::renderTextured3D() { Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D texture; Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - FlatGL3D shader{data.flags|flag}; - shader.bindTexture(texture); + FlatGL3D::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers && (data.flags & FlatGL3D::Flag::TextureArrays) && !(data.flags & FlatGL3D::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= FlatGL3D::Flag::TextureTransformation; + } + #endif + FlatGL3D shader{flags}; + + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags & FlatGL3D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); + shader.bindTexture(textureArray); + if(flag != FlatGL3D::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindTexture(texture); + } if(flag == FlatGL3D::Flag{}) { if(data.textureTransformation != Matrix3{}) @@ -1467,12 +1809,15 @@ template void FlatGLTest::renderTextured3D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layer) }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} .setColor(0x9999ff_rgbf) }}; - if(data.flags & FlatGL3D::Flag::TextureTransformation) + /* Also take into account the case when texture transform needs to be + enabled for texture arrays */ + if(flags & FlatGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -2122,6 +2467,11 @@ template void FlatGLTest::renderInstanced2D() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -2144,25 +2494,33 @@ template void FlatGLTest::renderInstanced2D() { struct { Matrix3 transformation; Color3 color; - Vector2 textureOffset; + Vector3 textureOffsetLayer; UnsignedInt objectId; } instanceData[] { {Matrix3::translation({-1.25f, -1.25f}), data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf, - {0.0f, 0.0f}, 211}, + {0.0f, 0.0f, 0.0f}, 211}, {Matrix3::translation({ 1.25f, -1.25f}), data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0x00ffff_rgbf, - {1.0f, 0.0f}, 4627}, + {1.0f, 0.0f, 1.0f}, 4627}, {Matrix3::translation({ 0.00f, 1.25f}), data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xff00ff_rgbf, - {0.5f, 1.0f}, 35363}, + #ifndef MAGNUM_TARGET_GLES2 + data.flags & FlatGL2D::Flag::TextureArrays ? Vector3{0.0f, 0.0f, 2.0f} : + #endif + Vector3{0.5f, 1.0f, 2.0f}, 35363}, }; circle .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, FlatGL2D::TransformationMatrix{}, FlatGL2D::Color3{}, + #ifndef MAGNUM_TARGET_GLES2 + FlatGL2D::TextureOffsetLayer{}, + #else FlatGL2D::TextureOffset{}, + 4, + #endif #ifndef MAGNUM_TARGET_GLES2 FlatGL2D::ObjectId{} #else @@ -2184,7 +2542,10 @@ template void FlatGLTest::renderInstanced2D() { #endif FlatGL2D shader{flags}; - GL::Texture2D texture; + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif if(data.flags & FlatGL3D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -2195,13 +2556,55 @@ template void FlatGLTest::renderInstanced2D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - shader.bindTexture(texture); + #ifndef MAGNUM_TARGET_GLES2 + /* For arrays we upload three slices of the original image to half-high + slices */ + if(data.flags & FlatGL2D::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Three slices with 2 extra as a base offset, each slice has + half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 2 + 3}) + .setSubImage(0, {0, 0, 2}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 3}, second) + .setSubImage(0, {0, 0, 4}, third); + shader.bindTexture(textureArray); + if(flag != FlatGL2D::Flag::UniformBuffers) + shader.setTextureLayer(2); /* base offset */ + + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindTexture(texture); + } } if(flag == FlatGL2D::Flag{}) { @@ -2212,7 +2615,13 @@ template void FlatGLTest::renderInstanced2D() { Matrix3::scaling(Vector2{0.4f})); if(data.flags & FlatGL3D::Flag::Textured) - shader.setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); + shader.setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & FlatGL2D::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f} + )); #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -2240,7 +2649,13 @@ template void FlatGLTest::renderInstanced2D() { }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & FlatGL2D::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f})) + .setLayer(2) /* base offset */ }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} @@ -2318,6 +2733,11 @@ template void FlatGLTest::renderInstanced3D() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -2340,7 +2760,7 @@ template void FlatGLTest::renderInstanced3D() { struct { Matrix4 transformation; Color3 color; - Vector2 textureOffset; + Vector3 textureOffsetLayer; UnsignedInt objectId; } instanceData[] { {Matrix4::translation({-1.25f, -1.25f, 0.0f})* @@ -2348,20 +2768,28 @@ template void FlatGLTest::renderInstanced3D() { normal matrix is applied properly */ Matrix4::rotationX(90.0_degf), data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf, - {0.0f, 0.0f}, 211}, + {0.0f, 0.0f, 0.0f}, 211}, {Matrix4::translation({ 1.25f, -1.25f, 0.0f}), data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0x00ffff_rgbf, - {1.0f, 0.0f}, 4627}, + {1.0f, 0.0f, 1.0f}, 4627}, {Matrix4::translation({ 0.0f, 1.0f, 1.0f}), data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0xff00ff_rgbf, - {0.5f, 1.0f}, 35363} + #ifndef MAGNUM_TARGET_GLES2 + data.flags & FlatGL2D::Flag::TextureArrays ? Vector3{0.0f, 0.0f, 2.0f} : + #endif + Vector3{0.5f, 1.0f, 2.0f}, 35363} }; sphere .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, FlatGL3D::TransformationMatrix{}, FlatGL3D::Color3{}, - FlatGL3D::TextureOffset{}, + #ifndef MAGNUM_TARGET_GLES2 + FlatGL2D::TextureOffsetLayer{}, + #else + FlatGL2D::TextureOffset{}, + 4, + #endif #ifndef MAGNUM_TARGET_GLES2 FlatGL2D::ObjectId{} #else @@ -2383,7 +2811,10 @@ template void FlatGLTest::renderInstanced3D() { #endif FlatGL3D shader{flags}; - GL::Texture2D texture; + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif if(data.flags & FlatGL2D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -2394,13 +2825,55 @@ template void FlatGLTest::renderInstanced3D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - shader.bindTexture(texture); + #ifndef MAGNUM_TARGET_GLES2 + /* For arrays we upload three slices of the original image to half-high + slices */ + if(data.flags & FlatGL2D::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Three slices with 2 extra as a base offset, each slice has + half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 2 + 3}) + .setSubImage(0, {0, 0, 2}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 3}, second) + .setSubImage(0, {0, 0, 4}, third); + shader.bindTexture(textureArray); + if(flag != FlatGL2D::Flag::UniformBuffers) + shader.setTextureLayer(2); /* base offset */ + + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindTexture(texture); + } } if(flag == FlatGL3D::Flag{}) { @@ -2412,7 +2885,13 @@ template void FlatGLTest::renderInstanced3D() { Matrix4::scaling(Vector3{0.4f})); if(data.flags & FlatGL3D::Flag::Textured) - shader.setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); + shader.setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & FlatGL2D::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f} + )); #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -2441,7 +2920,13 @@ template void FlatGLTest::renderInstanced3D() { }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & FlatGL2D::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f})) + .setLayer(2) /* base offset */ }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} @@ -2513,6 +2998,8 @@ void FlatGLTest::renderMulti2D() { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif if(data.flags >= FlatGL2D::Flag::MultiDraw) { @@ -2528,8 +3015,13 @@ void FlatGLTest::renderMulti2D() { #endif } - GL::Texture2D texture; - if(data.flags & FlatGL2D::Flag::Textured) { + FlatGL2D shader{FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId|data.flags, data.materialCount, data.drawCount}; + + GL::Texture2D texture{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray textureArray{NoCreate}; + #endif + if(data.flags & FlatGL3D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -2539,11 +3031,52 @@ void FlatGLTest::renderMulti2D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::RGB8, image->size()) - .setSubImage(0, {}, *image); + + #ifndef MAGNUM_TARGET_GLES2 + /* For arrays we upload three slices of the original image to half-high + slices */ + if(data.flags & FlatGL2D::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Each slice has half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 3}) + .setSubImage(0, {0, 0, 0}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 1}, second) + .setSubImage(0, {0, 0, 2}, third); + shader.bindTexture(textureArray); + + } else + #endif + { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindTexture(texture); + } } /* Circle is a fan, plane is a strip, make it indexed first */ @@ -2601,19 +3134,28 @@ void FlatGLTest::renderMulti2D() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({0.0f, 0.0f}) - ); + data.flags & FlatGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({1.0f, 0.0f}) - ); + data.flags & FlatGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({1.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f})) + .setLayer(1); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({0.5f, 1.0f}) - ); + data.flags & FlatGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f})) + .setLayer(2); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -2630,10 +3172,6 @@ void FlatGLTest::renderMulti2D() { .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - FlatGL2D shader{FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId|data.flags, data.materialCount, data.drawCount}; - if(data.flags & FlatGL2D::Flag::Textured) - shader.bindTexture(texture); - /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { shader.bindMaterialBuffer(materialUniform, @@ -2752,6 +3290,8 @@ void FlatGLTest::renderMulti3D() { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + if((data.flags & FlatGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif if(data.flags >= FlatGL3D::Flag::MultiDraw) { @@ -2767,7 +3307,10 @@ void FlatGLTest::renderMulti3D() { #endif } - GL::Texture2D texture; + FlatGL3D shader{FlatGL3D::Flag::UniformBuffers|FlatGL3D::Flag::ObjectId|data.flags, data.materialCount, data.drawCount}; + + GL::Texture2D texture{NoCreate}; + GL::Texture2DArray textureArray{NoCreate}; if(data.flags & FlatGL3D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -2778,11 +3321,49 @@ void FlatGLTest::renderMulti3D() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - texture.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::RGB8, image->size()) - .setSubImage(0, {}, *image); + + /* For arrays we upload three slices of the original image to half-high + slices */ + if(data.flags & FlatGL2D::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Each slice has half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 3}) + .setSubImage(0, {0, 0, 0}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 1}, second) + .setSubImage(0, {0, 0, 2}, third); + shader.bindTexture(textureArray); + + } else { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindTexture(texture); + } } Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, @@ -2846,19 +3427,28 @@ void FlatGLTest::renderMulti3D() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({0.0f, 0.0f}) - ); + data.flags & FlatGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({1.0f, 0.0f}) - ); + data.flags & FlatGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({1.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f})) + .setLayer(1); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({0.5f, 1.0f}) - ); + data.flags & FlatGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f})) + .setLayer(2); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -2875,10 +3465,6 @@ void FlatGLTest::renderMulti3D() { .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - FlatGL3D shader{FlatGL3D::Flag::UniformBuffers|FlatGL3D::Flag::ObjectId|data.flags, data.materialCount, data.drawCount}; - if(data.flags & FlatGL3D::Flag::Textured) - shader.bindTexture(texture); - /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { shader.bindMaterialBuffer(materialUniform, diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index b4389bbc4..a969e39b0 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -61,6 +61,7 @@ #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/MeshView.h" +#include "Magnum/GL/TextureArray.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/Primitives/Cone.h" @@ -94,10 +95,15 @@ struct PhongGLTest: GL::OpenGLTester { void setUniformUniformBuffersEnabled(); void bindBufferUniformBuffersNotEnabled(); #endif - void bindTexturesNotEnabled(); + void bindTexturesInvalid(); + #ifndef MAGNUM_TARGET_GLES2 + void bindTextureArraysInvalid(); + #endif void setAlphaMaskNotEnabled(); void setTextureMatrixNotEnabled(); + void setNormalTextureScaleNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 + void setTextureLayerNotArray(); void bindTextureTransformBufferNotEnabled(); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -171,19 +177,20 @@ struct PhongGLTest: GL::OpenGLTester { [I] instancing [O] UBOs + draw offset [M] multidraw + [L] texture arrays - Mesa Intel BADLIOM - ES2 xx + Mesa Intel BADLIOML + ES2 xxx ES3 BADL Ox Mesa AMD BAD Mesa llvmpipe BAD - SwiftShader ES2 BADL xx + SwiftShader ES2 BADL xxx ES3 BADL - ANGLE ES2 xx + ANGLE ES2 xxx ES3 BADL OM - ARM Mali (Huawei P10) ES2 BAD xx + ARM Mali (Huawei P10) ES2 BAD xxx ES3 BADL Ox - WebGL (on Mesa Intel) 1.0 BAD xx + WebGL (on Mesa Intel) 1.0 BAD xxx 2.0 BADL OM NVidia BAD Intel Windows BAD @@ -210,6 +217,10 @@ constexpr struct { {"diffuse + specular texture", PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1}, {"ambient + diffuse + specular texture", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1}, {"ambient + diffuse + specular + normal texture", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"ambient + diffuse + specular + normal texture arrays", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays, 1}, + {"ambient + diffuse + specular + normal texture arrays + texture transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1}, + #endif {"alpha mask", PhongGL::Flag::AlphaMask, 1}, {"alpha mask + diffuse texture", PhongGL::Flag::AlphaMask|PhongGL::Flag::DiffuseTexture, 1}, {"vertex colors", PhongGL::Flag::VertexColor, 1}, @@ -245,11 +256,12 @@ constexpr struct { {"zero lights", PhongGL::Flag::UniformBuffers, 0, 16, 24}, {"ambient + diffuse + specular texture", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1, 1, 1}, {"ambient + diffuse + specular texture + texture transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, + {"ambient + diffuse + specular texture array + texture transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1}, {"normal texture", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture, 1, 1, 1}, {"normal texture + separate bitangents", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1}, {"alpha mask", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AlphaMask, 1, 1, 1}, {"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1}, - {"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 8, 16, 24} + {"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 8, 16, 24} }; #endif @@ -262,6 +274,8 @@ constexpr struct { PhongGL::Flag::TextureTransformation, "texture transformation enabled but the shader is not textured"}, #ifndef MAGNUM_TARGET_GLES2 + {"texture arrays but not textured", PhongGL::Flag::TextureArrays, + "texture arrays enabled but the shader is not textured"}, {"conflicting bitangent and instanced object id attribute", PhongGL::Flag::Bitangent|PhongGL::Flag::InstancedObjectId, "Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead"}, @@ -279,6 +293,48 @@ constexpr struct { "draw count can't be zero"}, {"zero materials", PhongGL::Flag::UniformBuffers, 1, 0, 1, "material count can't be zero"}, + {"texture arrays but no transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, 1, 1, 1, + "texture arrays require texture transformation enabled as well if uniform buffers are used"} +}; +#endif + +constexpr struct { + const char* name; + PhongGL::Flags flags; + const char* message; +} BindTexturesInvalidData[]{ + {"not textured", {}, + "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled\n" + "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled\n" + "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled\n" + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled\n" + "Shaders::PhongGL::bindTextures(): the shader was not created with any textures enabled\n"}, + #ifndef MAGNUM_TARGET_GLES2 + {"array", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays, + "Shaders::PhongGL::bindAmbientTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" + "Shaders::PhongGL::bindDiffuseTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" + "Shaders::PhongGL::bindSpecularTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" + "Shaders::PhongGL::bindNormalTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" + "Shaders::PhongGL::bindTextures(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n"} + #endif +}; + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + PhongGL::Flags flags; + const char* message; +} BindTextureArraysInvalidData[]{ + {"not textured", {}, + "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled\n" + "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled\n" + "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled\n" + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled\n"}, + {"not array", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture, + "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" + "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" + "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n"} }; #endif @@ -297,10 +353,16 @@ const struct { constexpr struct { const char* name; + PhongGL::Flags flags; + Int layer; bool multiBind; } RenderSinglePixelTexturedData[]{ - {"", false}, - {"multi bind", true} + {"", {}, 0, false}, + {"multi bind", {}, 0, true}, + #ifndef MAGNUM_TARGET_GLES2 + {"array, first layer", PhongGL::Flag::TextureArrays, 0, false}, + {"array, arbitrary layer", PhongGL::Flag::TextureArrays, 6, false}, + #endif }; const struct { @@ -308,15 +370,31 @@ const struct { const char* expected; PhongGL::Flags flags; Matrix3 textureTransformation; + Int layer; } RenderTexturedData[]{ - {"all", "textured.tga", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, {}}, - {"ambient", "textured-ambient.tga", PhongGL::Flag::AmbientTexture, {}}, - {"diffuse", "textured-diffuse.tga", PhongGL::Flag::DiffuseTexture, {}}, + {"all", "textured.tga", + PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, + {}, 0}, + {"ambient", "textured-ambient.tga", PhongGL::Flag::AmbientTexture, + {}, 0}, + {"diffuse", "textured-diffuse.tga", PhongGL::Flag::DiffuseTexture, + {}, 0}, {"diffuse transformed", "textured-diffuse-transformed.tga", PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureTransformation, - Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}) - }, - {"specular", "textured-specular.tga", PhongGL::Flag::SpecularTexture, {}} + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), 0}, + {"specular", "textured-specular.tga", PhongGL::Flag::SpecularTexture, + {}, 0}, + #ifndef MAGNUM_TARGET_GLES2 + {"all, array, first layer", "textured.tga", + PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays, + {}, 0}, + {"all, array, arbitrary layer", "textured.tga", + PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays, + {}, 6}, + {"diffuse, array, texture transformation, arbitrary layer", "textured-diffuse-transformed.tga", + PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), 6}, + #endif }; /* MSVC 2015 doesn't like constexpr here due to the angles */ @@ -331,47 +409,58 @@ const struct { PhongGL::Tangent4::Components tangentComponents; bool flipNormalY; PhongGL::Flags flags; + Int layer; } RenderTexturedNormalData[]{ {"", "textured-normal.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, {"multi bind", "textured-normal.tga", true, {}, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture arrays, first layer", "textured-normal.tga", false, {}, 1.0f, + {1.0f, 0.0f, 0.0f, 1.0f}, {}, + PhongGL::Tangent4::Components::Four, false, + PhongGL::Flag::TextureArrays, 0}, + {"texture arrays, arbitrary layer", "textured-normal.tga", false, {}, 1.0f, + {1.0f, 0.0f, 0.0f, 1.0f}, {}, + PhongGL::Tangent4::Components::Four, false, + PhongGL::Flag::TextureArrays, 6}, + #endif {"rotated 90°", "textured-normal.tga", false, 90.0_degf, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, {"rotated -90°", "textured-normal.tga", false, -90.0_degf, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, {"0.5 scale", "textured-normal0.5.tga", false, {}, 0.5f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, {"0.0 scale", "textured-normal0.0.tga", false, {}, 0.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, /* The fourth component, if missing, gets automatically filled up to 1, so this should work */ {"implicit bitangent direction", "textured-normal.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, 0.0f}, {}, - PhongGL::Tangent4::Components::Three, false, {}}, + PhongGL::Tangent4::Components::Three, false, {}, 0}, {"separate bitangents", "textured-normal.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, PhongGL::Tangent4::Components::Three, false, - PhongGL::Flag::Bitangent}, + PhongGL::Flag::Bitangent, 0}, {"right-handed, flipped Y", "textured-normal-left.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}, {}, - PhongGL::Tangent4::Components::Four, true, {}}, + PhongGL::Tangent4::Components::Four, true, {}, 0}, {"left-handed", "textured-normal-left.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, -1.0f}, {}, - PhongGL::Tangent4::Components::Four, false, {}}, + PhongGL::Tangent4::Components::Four, false, {}, 0}, {"left-handed, separate bitangents", "textured-normal-left.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, PhongGL::Tangent4::Components::Three, false, - PhongGL::Flag::Bitangent}, + PhongGL::Flag::Bitangent, 0}, {"left-handed, flipped Y", "textured-normal.tga", false, {}, 1.0f, {1.0f, 0.0f, 0.0f, -1.0f}, {}, - PhongGL::Tangent4::Components::Four, true, {}} + PhongGL::Tangent4::Components::Four, true, {}, 0}, }; const struct { @@ -432,6 +521,7 @@ const struct { PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AlphaMask, 1.0f, "alpha-texture.tga", "diffuse-texture.tga", 0xffffffff_rgbaf, 0x9999ff00_rgbaf} + /* texture arrays are orthogonal to this, no need to be tested here */ }; const struct { @@ -564,6 +654,14 @@ constexpr struct { /* Minor differences on SwiftShader */ 112.0f, 0.09f}, /** @todo test normal when there's usable texture */ + #ifndef MAGNUM_TARGET_GLES2 + {"diffuse texture array", "instanced-textured.tga", + PhongGL::Flag::DiffuseTexture|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::TextureArrays, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around); minor differences on + SwiftShader */ + 112.0f, 0.099f} + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -585,6 +683,14 @@ constexpr struct { 2, 1, 1, 16, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, + #ifndef MAGNUM_TARGET_GLES2 + {"bind with offset, texture array", "multidraw-textured.tga", + PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 2, 1, 1, 16, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 50.34f, 0.131f}, + #endif {"draw offset, colored", "multidraw.tga", {}, 4, 2, 3, 1, @@ -595,6 +701,14 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, + #ifndef MAGNUM_TARGET_GLES2 + {"draw offset, texture array", "multidraw-textured.tga", + PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 4, 2, 3, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 50.34f, 0.131f}, + #endif {"multidraw, colored", "multidraw.tga", PhongGL::Flag::MultiDraw, 4, 2, 3, 1, @@ -605,6 +719,14 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, + #ifndef MAGNUM_TARGET_GLES2 + {"multidraw, texture array", "multidraw-textured.tga", + PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, + 4, 2, 3, 1, + /* Some difference at the UV edge (texture is wrapping in the 2D case + while the 2D array has a black area around) */ + 50.34f, 0.131f}, + #endif /** @todo test normal and per-draw scaling when there's usable texture */ }; #endif @@ -634,14 +756,26 @@ PhongGLTest::PhongGLTest() { Containers::arraySize(ConstructUniformBuffersInvalidData)); #endif + #ifndef MAGNUM_TARGET_GLES2 + addTests({&PhongGLTest::setUniformUniformBuffersEnabled, + &PhongGLTest::bindBufferUniformBuffersNotEnabled}); + #endif + + addInstancedTests({&PhongGLTest::bindTexturesInvalid}, + Containers::arraySize(BindTexturesInvalidData)); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&PhongGLTest::bindTextureArraysInvalid}, + Containers::arraySize(BindTextureArraysInvalidData)); + #endif + addTests({ - #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::setUniformUniformBuffersEnabled, - &PhongGLTest::bindBufferUniformBuffersNotEnabled, - #endif - &PhongGLTest::bindTexturesNotEnabled, &PhongGLTest::setAlphaMaskNotEnabled, &PhongGLTest::setTextureMatrixNotEnabled, + &PhongGLTest::setNormalTextureScaleNotEnabled, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::setTextureLayerNotArray, + #endif #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::bindTextureTransformBufferNotEnabled, #endif @@ -841,6 +975,8 @@ void PhongGLTest::construct() { #ifndef MAGNUM_TARGET_GLES if((data.flags & PhongGL::Flag::ObjectId) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif PhongGL shader{data.flags, data.lightCount}; @@ -867,6 +1003,8 @@ void PhongGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); if((data.flags & PhongGL::Flag::ObjectId) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif if(data.flags >= PhongGL::Flag::MultiDraw) { @@ -1015,6 +1153,7 @@ void PhongGLTest::setUniformUniformBuffersEnabled() { .setNormalMatrix({}) .setProjectionMatrix({}) .setTextureMatrix({}) + .setTextureLayer({}) .setLightPositions(std::initializer_list{}) .setLightPosition(0, Vector4{}) .setLightColors(std::initializer_list{}) @@ -1035,6 +1174,7 @@ void PhongGLTest::setUniformUniformBuffersEnabled() { "Shaders::PhongGL::setNormalMatrix(): the shader was created with uniform buffers enabled\n" "Shaders::PhongGL::setProjectionMatrix(): the shader was created with uniform buffers enabled\n" "Shaders::PhongGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setTextureLayer(): the shader was created with uniform buffers enabled\n" "Shaders::PhongGL::setLightPositions(): the shader was created with uniform buffers enabled\n" "Shaders::PhongGL::setLightPosition(): the shader was created with uniform buffers enabled\n" "Shaders::PhongGL::setLightColors(): the shader was created with uniform buffers enabled\n" @@ -1085,31 +1225,60 @@ void PhongGLTest::bindBufferUniformBuffersNotEnabled() { } #endif -void PhongGLTest::bindTexturesNotEnabled() { +void PhongGLTest::bindTexturesInvalid() { + auto&& data = BindTexturesInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + std::ostringstream out; Error redirectError{&out}; GL::Texture2D texture; - PhongGL shader; + PhongGL shader{data.flags}; shader.bindAmbientTexture(texture) .bindDiffuseTexture(texture) .bindSpecularTexture(texture) .bindNormalTexture(texture) - .setNormalTextureScale(0.5f) .bindTextures(&texture, &texture, &texture, &texture); - CORRADE_COMPARE(out.str(), - "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled\n" - "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled\n" - "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled\n" - "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled\n" - "Shaders::PhongGL::setNormalTextureScale(): the shader was not created with normal texture enabled\n" - "Shaders::PhongGL::bindTextures(): the shader was not created with any textures enabled\n"); + CORRADE_COMPARE(out.str(), data.message); +} + +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::bindTextureArraysInvalid() { + auto&& data = BindTextureArraysInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Texture2DArray textureArray; + PhongGL shader{data.flags}; + shader.bindAmbientTexture(textureArray) + .bindDiffuseTexture(textureArray) + .bindSpecularTexture(textureArray) + .bindNormalTexture(textureArray); + + CORRADE_COMPARE(out.str(), data.message); } +#endif void PhongGLTest::setAlphaMaskNotEnabled() { #ifdef CORRADE_NO_ASSERT @@ -1141,6 +1310,38 @@ void PhongGLTest::setTextureMatrixNotEnabled() { "Shaders::PhongGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); } +void PhongGLTest::setNormalTextureScaleNotEnabled() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + PhongGL shader; + shader.setNormalTextureScale({}); + + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::setNormalTextureScale(): the shader was not created with normal texture enabled\n"); +} + +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::setTextureLayerNotArray() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + PhongGL shader; + shader.setTextureLayer(37); + + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::setTextureLayer(): the shader was not created with texture arrays enabled\n"); +} +#endif + #ifndef MAGNUM_TARGET_GLES2 void PhongGLTest::bindTextureTransformBufferNotEnabled() { #ifdef CORRADE_NO_ASSERT @@ -1457,43 +1658,92 @@ template void PhongGLTest::renderSinglePixelTextured() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); + PhongGL::Flags flags = PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= PhongGL::Flag::TextureTransformation; + } + #endif + PhongGL shader{flags, 2}; + const Color4ub ambientData[]{ 0x330033_rgb }; ImageView2D ambientImage{PixelFormat::RGBA8Unorm, Vector2i{1}, ambientData}; - GL::Texture2D ambient; - ambient.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGBA, Vector2i{1}) - .setSubImage(0, {}, ambientImage); const Color4ub diffuseData[]{ 0xccffcc_rgb }; ImageView2D diffuseImage{PixelFormat::RGBA8Unorm, Vector2i{1}, diffuseData}; - GL::Texture2D diffuse; - diffuse.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGBA, Vector2i{1}) - .setSubImage(0, {}, diffuseImage); const Color4ub specularData[]{ 0x6666ff_rgb }; ImageView2D specularImage{PixelFormat::RGBA8Unorm, Vector2i{1}, specularData}; - GL::Texture2D specular; - specular.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGBA, Vector2i{1}) - .setSubImage(0, {}, specularImage); - PhongGL shader{PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|flag, 2}; - if(data.multiBind) - shader.bindTextures(&ambient, &diffuse, &specular, nullptr); - else shader - .bindAmbientTexture(ambient) - .bindDiffuseTexture(diffuse) - .bindSpecularTexture(specular); + GL::Texture2D ambient; + GL::Texture2D diffuse; + GL::Texture2D specular; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray ambientArray{NoCreate}; + GL::Texture2DArray diffuseArray{NoCreate}; + GL::Texture2DArray specularArray{NoCreate}; + if(data.flags & PhongGL::Flag::TextureArrays) { + ambientArray = GL::Texture2DArray{}; + ambientArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ambientImage); + diffuseArray = GL::Texture2DArray{}; + diffuseArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, diffuseImage); + specularArray = GL::Texture2DArray{}; + specularArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector3i{1, 1, data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, specularImage); + shader + .bindAmbientTexture(ambientArray) + .bindDiffuseTexture(diffuseArray) + .bindSpecularTexture(specularArray); + if(flag != PhongGL::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else + #endif + { + ambient = GL::Texture2D{}; + ambient.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, ambientImage); + diffuse = GL::Texture2D{}; + diffuse.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, diffuseImage); + specular = GL::Texture2D{}; + specular.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, specularImage); + if(data.multiBind) + shader.bindTextures(&ambient, &diffuse, &specular, nullptr); + else shader + .bindAmbientTexture(ambient) + .bindDiffuseTexture(diffuse) + .bindSpecularTexture(specular); + } if(flag == PhongGL::Flag{}) { shader.setLightColors({0x993366_rgbf, 0x669933_rgbf}) @@ -1516,6 +1766,10 @@ template void PhongGLTest::renderSinglePixelTextured() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { PhongDrawUniform{} }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setLayer(data.layer) + }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { PhongMaterialUniform{} /* Has to be set because the default is black regardless of @@ -1531,6 +1785,10 @@ template void PhongGLTest::renderSinglePixelTextured() { .setPosition({ 3.0f, -3.0f, 2.0f, 0.0f}) .setColor(0x669933_rgbf) }}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) @@ -1566,6 +1824,11 @@ template void PhongGLTest::renderTextured() { auto&& data = RenderTexturedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + #ifndef MAGNUM_TARGET_GLES2 if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1584,47 +1847,104 @@ template void PhongGLTest::renderTextured() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); - PhongGL shader{data.flags|flag, 2}; + PhongGL::Flags flags = data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= PhongGL::Flag::TextureTransformation; + } + #endif + PhongGL shader{flags, 2}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); - GL::Texture2D ambient; + GL::Texture2D ambient{NoCreate}; + GL::Texture2D diffuse{NoCreate}; + GL::Texture2D specular{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray ambientArray{NoCreate}; + GL::Texture2DArray diffuseArray{NoCreate}; + GL::Texture2DArray specularArray{NoCreate}; + #endif if(data.flags & PhongGL::Flag::AmbientTexture) { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/ambient-texture.tga")) && (image = importer->image2D(0))); - ambient.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - shader.bindAmbientTexture(ambient); + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & PhongGL::Flag::TextureArrays) { + ambientArray = GL::Texture2DArray{}; + ambientArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); + shader.bindAmbientTexture(ambientArray); + } else + #endif + { + ambient = GL::Texture2D{}; + ambient.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindAmbientTexture(ambient); + } } /* If no diffuse texture is present, dial down the default diffuse color so ambient/specular is visible */ - GL::Texture2D diffuse; if(data.flags & PhongGL::Flag::DiffuseTexture) { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - diffuse.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - shader.bindDiffuseTexture(diffuse); + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & PhongGL::Flag::TextureArrays) { + diffuseArray = GL::Texture2DArray{}; + diffuseArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); + shader.bindDiffuseTexture(diffuseArray); + } else + #endif + { + diffuse = GL::Texture2D{}; + diffuse.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindDiffuseTexture(diffuse); + } } - GL::Texture2D specular; if(data.flags & PhongGL::Flag::SpecularTexture) { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/specular-texture.tga")) && (image = importer->image2D(0))); - specular.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - shader.bindSpecularTexture(specular); + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & PhongGL::Flag::TextureArrays) { + specularArray = GL::Texture2DArray{}; + specularArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); + shader.bindSpecularTexture(specularArray); + } else + #endif + { + specular = GL::Texture2D{}; + specular.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindSpecularTexture(specular); + } } if(flag == PhongGL::Flag{}) { @@ -1643,6 +1963,10 @@ template void PhongGLTest::renderTextured() { /* Colorized. Case without a color (where it should be white) is tested in renderSinglePixelTextured() */ shader.setSpecularColor(0x99ff99_rgbf); + #ifndef MAGNUM_TARGET_GLES2 + if(data.layer != 0) /* to verify the default */ + shader.setTextureLayer(data.layer); + #endif /* Using default (white) light colors to have the texture data visible better */ @@ -1680,6 +2004,7 @@ template void PhongGLTest::renderTextured() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) + .setLayer(data.layer) }}; GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { PhongLightUniform{}.setPosition({-3.0f, -3.0f, 2.0f, 0.0f}), @@ -1697,7 +2022,9 @@ template void PhongGLTest::renderTextured() { materialUniformData->setSpecularColor(0x99ff99_rgbf); GL::Buffer materialUniform{materialUniformData}; - if(data.textureTransformation != Matrix3{}) + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & PhongGL::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) @@ -1741,6 +2068,11 @@ template void PhongGLTest::renderTexturedNormal() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1755,12 +2087,40 @@ template void PhongGLTest::renderTexturedNormal() { for(Color3ub& pixel: row) pixel.y() = 255 - pixel.y(); - GL::Texture2D normal; - normal.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); + PhongGL::Flags flags = PhongGL::Flag::NormalTexture|data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= PhongGL::Flag::TextureTransformation; + } + #endif + PhongGL shader{flags, 2}; + + GL::Texture2D normal{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray normalArray{NoCreate}; + if(data.flags & PhongGL::Flag::TextureArrays) { + normalArray = GL::Texture2DArray{}; + normalArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, {image->size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, ImageView2D{*image}); + shader.bindNormalTexture(normalArray); + } else + #endif + { + normal = GL::Texture2D{}; + normal.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + if(data.multiBind) + shader.bindTextures(nullptr, nullptr, nullptr, &normal); + else + shader.bindNormalTexture(normal); + } GL::Mesh plane = MeshTools::compile(Primitives::planeSolid( Primitives::PlaneFlag::TextureCoordinates)); @@ -1781,16 +2141,14 @@ template void PhongGLTest::renderTexturedNormal() { /* Rotating the view a few times (together with light positions). If the tangent transformation in the shader is correct, it should result in exactly the same images. */ - PhongGL shader{PhongGL::Flag::NormalTexture|data.flags|flag, 2}; - if(data.multiBind) - shader.bindTextures(nullptr, nullptr, nullptr, &normal); - else - shader.bindNormalTexture(normal); - if(flag == PhongGL::Flag{}) { - /* Verify the default is working properly */ + /* Verify the defaults are working properly */ if(data.scale != 1.0f) shader.setNormalTextureScale(data.scale); + #ifndef MAGNUM_TARGET_GLES2 + if(data.layer != 0) + shader.setTextureLayer(data.layer); + #endif shader.setLightPositions({ Matrix4::rotationZ(data.rotation)*Vector4{-3.0f, -3.0f, 2.0f, 0.0f}, @@ -1833,10 +2191,18 @@ template void PhongGLTest::renderTexturedNormal() { .setDiffuseColor(0x999999_rgbf) .setNormalTextureScale(data.scale) }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setLayer(data.layer) + }}; GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { PhongLightUniform{}.setPosition(Matrix4::rotationZ(data.rotation)*Vector4{-3.0f, -3.0f, 2.0f, 0.0f}), PhongLightUniform{}.setPosition(Matrix4::rotationZ(data.rotation)*Vector4{3.0f, -3.0f, 2.0f, 0.0f}) }}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) @@ -2810,6 +3176,11 @@ template void PhongGLTest::renderInstanced() { } #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -2836,7 +3207,7 @@ template void PhongGLTest::renderInstanced() { Matrix4 transformation; Matrix3x3 normal; Color3 color; - Vector2 textureOffset; + Vector3 textureOffsetLayer; UnsignedInt objectId; } instanceData[] { {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{-1.25f, -1.25f, 0.0f}))*Matrix4::rotationY(-90.0_degf)*Matrix4::rotationX(90.0_degf), @@ -2845,15 +3216,18 @@ template void PhongGLTest::renderInstanced() { instanced textured */ (Matrix4::rotationY(-90.0_degf)*Matrix4::rotationX(90.0_degf)).normalMatrix(), data.flags & PhongGL::Flag::DiffuseTexture ? 0xffffff_rgbf : 0xffff00_rgbf, - {0.0f, 0.0f}, 211}, + {0.0f, 0.0f, 0.0f}, 211}, {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{ 1.25f, -1.25f, 0.0f})), {}, data.flags & PhongGL::Flag::DiffuseTexture ? 0xffffff_rgbf : 0x00ffff_rgbf, - {1.0f, 0.0f}, 4627}, + {1.0f, 0.0f, 1.0f}, 4627}, {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{ 0.0f, 1.0f, -1.0f})), {}, data.flags & PhongGL::Flag::DiffuseTexture ? 0xffffff_rgbf : 0xff00ff_rgbf, - {0.5f, 1.0f}, 35363} + #ifndef MAGNUM_TARGET_GLES2 + data.flags & PhongGL::Flag::TextureArrays ? Vector3{0.0f, 0.0f, 2.0f} : + #endif + Vector3{0.5f, 1.0f, 2.0f}, 35363} }; sphere @@ -2861,7 +3235,12 @@ template void PhongGLTest::renderInstanced() { PhongGL::TransformationMatrix{}, PhongGL::NormalMatrix{}, PhongGL::Color3{}, + #ifndef MAGNUM_TARGET_GLES2 + PhongGL::TextureOffsetLayer{}, + #else PhongGL::TextureOffset{}, + 4, + #endif #ifndef MAGNUM_TARGET_GLES2 PhongGL::ObjectId{} #else @@ -2883,8 +3262,12 @@ template void PhongGLTest::renderInstanced() { #endif PhongGL shader{flags, 2}; - GL::Texture2D diffuse; - GL::Texture2D normal; + GL::Texture2D diffuse{NoCreate}; + GL::Texture2D normal{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray diffuseArray{NoCreate}; + GL::Texture2DArray normalArray{NoCreate}; + #endif if(data.flags & (PhongGL::Flag::DiffuseTexture|PhongGL::Flag::NormalTexture)) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -2896,17 +3279,104 @@ template void PhongGLTest::renderInstanced() { if(data.flags & PhongGL::Flag::DiffuseTexture) { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - diffuse.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - shader.bindDiffuseTexture(diffuse); + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & PhongGL::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + diffuseArray = GL::Texture2DArray{}; + diffuseArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Three slices with 2 extra as a base offset, each slice + has half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 2 + 3}) + .setSubImage(0, {0, 0, 2}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 3}, second) + .setSubImage(0, {0, 0, 4}, third); + shader.bindDiffuseTexture(diffuseArray); + if(flag != PhongGL::Flag::UniformBuffers) + shader.setTextureLayer(2); /* base offset */ + + } else + #endif + { + diffuse = GL::Texture2D{}; + diffuse.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindDiffuseTexture(diffuse); + } } if(data.flags & PhongGL::Flag::NormalTexture) { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/normal-texture.tga")) && (image = importer->image2D(0))); + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & PhongGL::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + normalArray = GL::Texture2DArray{}; + normalArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Three slices with 2 extra as a base offset, each slice + has half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 2 + 3}) + .setSubImage(0, {0, 0, 2}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 3}, second) + .setSubImage(0, {0, 0, 4}, third); + shader.bindNormalTexture(normalArray); + + } else + #endif + { + normal = GL::Texture2D{}; + normal.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindNormalTexture(normal); + } + normal.setMinificationFilter(GL::SamplerFilter::Linear) .setMagnificationFilter(GL::SamplerFilter::Linear) .setWrapping(GL::SamplerWrapping::ClampToEdge) @@ -2915,7 +3385,6 @@ template void PhongGLTest::renderInstanced() { shader.bindNormalTexture(normal); } } - if(flag == PhongGL::Flag{}) { shader .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, @@ -2933,7 +3402,17 @@ template void PhongGLTest::renderInstanced() { 0xffffff_rgbf : 0xffff00_rgbf); if(data.flags & PhongGL::Flag::TextureTransformation) - shader.setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); + shader.setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & PhongGL::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f} + )); + #ifndef MAGNUM_TARGET_GLES2 + if((data.flags & PhongGL::Flag::TextureArrays) && flag != PhongGL::Flag::UniformBuffers) + shader.setTextureLayer(2); /* base offset */ + #endif #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -2972,7 +3451,13 @@ template void PhongGLTest::renderInstanced() { }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & PhongGL::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f})) + .setLayer(2) /* base offset */ }}; GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { PhongLightUniform{} @@ -3051,6 +3536,8 @@ void PhongGLTest::renderMulti() { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + if((data.flags & PhongGL::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); #endif if(data.flags >= PhongGL::Flag::MultiDraw) { @@ -3066,7 +3553,10 @@ void PhongGLTest::renderMulti() { #endif } - GL::Texture2D diffuse; + PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|data.flags, data.lightCount, data.materialCount, data.drawCount}; + + GL::Texture2D diffuse{NoCreate}; + GL::Texture2DArray diffuseArray{NoCreate}; if(data.flags & PhongGL::Flag::DiffuseTexture) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -3078,11 +3568,48 @@ void PhongGLTest::renderMulti() { Containers::Optional image; CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - diffuse.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, GL::TextureFormat::RGB8, image->size()) - .setSubImage(0, {}, *image); + /* For arrays we upload three slices of the original image to half-high + slices */ + if(data.flags & PhongGL::Flag::TextureArrays) { + /** @todo implement image slicing, ffs */ + const ImageView2D first{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({0, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D second{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/2, 0, 0}), + image->format(), image->size()/2, image->data()}; + const ImageView2D third{ + image->storage().setRowLength(image->size().x()) + .setImageHeight(image->size().y()) + .setSkip({image->size().x()/4, image->size().y()/2, 0}), + image->format(), image->size()/2, image->data()}; + + diffuseArray = GL::Texture2DArray{}; + diffuseArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + /* Each slice has half the height */ + .setStorage(1, TextureFormatRGB, {image->size().x(), image->size().y()/2, 3}) + .setSubImage(0, {0, 0, 0}, first) + /* Put the second image on the right half to test that the + per-instance offset is used together with the layer */ + .setSubImage(0, {image->size().x()/2, 0, 1}, second) + .setSubImage(0, {0, 0, 2}, third); + shader.bindDiffuseTexture(diffuseArray); + + } else { + diffuse = GL::Texture2D{}; + diffuse.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindDiffuseTexture(diffuse); + } } Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, @@ -3175,19 +3702,28 @@ void PhongGLTest::renderMulti() { Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({0.0f, 0.0f}) - ); + data.flags & PhongGL::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f})) + .setLayer(0); /* ignored if not array */ textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({1.0f, 0.0f}) - ); + data.flags & PhongGL::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({1.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f})) + .setLayer(1); /* ignored if not array */ textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} .setTextureMatrix( - Matrix3::scaling(Vector2{0.5f})* - Matrix3::translation({0.5f, 1.0f}) - ); + data.flags & PhongGL::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f})) + .setLayer(2); /* ignored if not array */ GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; @@ -3210,10 +3746,7 @@ void PhongGLTest::renderMulti() { .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|data.flags, data.lightCount, data.materialCount, data.drawCount}; shader.bindProjectionBuffer(projectionUniform); - if(data.flags & PhongGL::Flag::DiffuseTexture) - shader.bindDiffuseTexture(diffuse); /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { diff --git a/src/Magnum/Shaders/generic.glsl b/src/Magnum/Shaders/generic.glsl index 985220296..5acb24f38 100644 --- a/src/Magnum/Shaders/generic.glsl +++ b/src/Magnum/Shaders/generic.glsl @@ -35,7 +35,7 @@ #define TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION 8 #define NORMAL_MATRIX_ATTRIBUTE_LOCATION 12 -#define TEXTURE_OFFSET_ATTRIBUTE_LOCATION 15 +#define TEXTURE_OFFSET_ATTRIBUTE_LOCATION 15 /* + layer in the 3rd component */ /* Outputs */ #define COLOR_OUTPUT_ATTRIBUTE_LOCATION 0