diff --git a/doc/changelog.dox b/doc/changelog.dox index e44340866..151f27d93 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -233,6 +233,7 @@ See also: @ref Shaders::Generic::TransformationMatrix, @ref Shaders::Generic::NormalMatrix and @ref Shaders::Generic::TextureOffset for instancing +- Instancing in @ref Shaders::Flat @subsubsection changelog-latest-new-trade Trade library diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index b6174b29d..a871a4094 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -249,6 +249,26 @@ framebuffer.mapForDraw({ } #endif +{ +GL::Mesh mesh; +/* [Flat-usage-instancing] */ +struct { + Matrix4 transformation; + Color3 color; +} instanceData[] { + {Matrix4::translation({1.0f, 2.0f, 0.0f}), 0xff3333_rgbf}, + {Matrix4::translation({2.0f, 1.0f, 0.0f}), 0x33ff33_rgbf}, + {Matrix4::translation({3.0f, 0.0f, 1.0f}), 0x3333ff_rgbf}, + // ... +}; + +mesh.setInstanceCount(Containers::arraySize(instanceData)) + .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + Shaders::Flat3D::TransformationMatrix{}, + Shaders::Flat3D::Color3{}); +/* [Flat-usage-instancing] */ +} + { /* [MeshVisualizer-usage-geom1] */ struct Vertex { diff --git a/src/Magnum/Shaders/Flat.cpp b/src/Magnum/Shaders/Flat.cpp index f126cba5e..f9c5c9e56 100644 --- a/src/Magnum/Shaders/Flat.cpp +++ b/src/Magnum/Shaders/Flat.cpp @@ -72,6 +72,8 @@ template Flat::Flat(const Flags flags): _fla #ifndef MAGNUM_TARGET_GLES2 .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif + .addSource(flags & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "") + .addSource(flags >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : "") .addSource(rs.get("generic.glsl")) .addSource(rs.get("Flat.vert")); frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") @@ -107,6 +109,10 @@ template Flat::Flat(const Flags flags): _fla } if(flags >= Flag::InstancedObjectId) bindAttributeLocation(ObjectId::Location, "instanceObjectId"); + if(flags & Flag::InstancedTransformation) + bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix"); + if(flags >= Flag::InstancedTextureOffset) + bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset"); #endif } #endif @@ -203,6 +209,8 @@ Debug& operator<<(Debug& debug, const FlatFlag value) { _c(ObjectId) _c(InstancedObjectId) #endif + _c(InstancedTransformation) + _c(InstancedTextureOffset) #undef _c /* LCOV_EXCL_STOP */ } @@ -215,12 +223,13 @@ Debug& operator<<(Debug& debug, const FlatFlags value) { FlatFlag::Textured, FlatFlag::AlphaMask, FlatFlag::VertexColor, + FlatFlag::InstancedTextureOffset, /* Superset of TextureTransformation */ FlatFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 FlatFlag::InstancedObjectId, /* Superset of ObjectId */ - FlatFlag::ObjectId + FlatFlag::ObjectId, #endif - }); + FlatFlag::InstancedTransformation}); } } diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index e8a2158f2..78ea0dced 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -44,8 +44,10 @@ namespace Implementation { TextureTransformation = 1 << 3, #ifndef MAGNUM_TARGET_GLES2 ObjectId = 1 << 4, - InstancedObjectId = (1 << 5)|ObjectId + InstancedObjectId = (1 << 5)|ObjectId, #endif + InstancedTransformation = 1 << 6, + InstancedTextureOffset = (1 << 7)|TextureTransformation }; typedef Containers::EnumSet FlatFlags; } @@ -120,6 +122,28 @@ from @ref setObjectId(). @requires_gles30 Object ID output requires integer buffer attachments, which are not available in OpenGL ES 2.0 or WebGL 1.0. +@section Shaders-Flat-instancing Instanced rendering + +Enabling @ref Flag::InstancedTransformation will turn the shader into an +instanced one. It'll take per-instance transformation from the +@ref TransformationMatrix attribute, applying it before the matrix set by +@ref setTransformationProjectionMatrix(). Besides that, @ref Flag::VertexColor +(and the @ref Color3 / @ref Color4) attributes can work as both per-vertex and +per-instance, and for texturing it's possible to have per-instance texture +offset taken from @ref TextureOffset when @ref Flag::InstancedTextureOffset is +enabled (similarly to transformation, applied before @ref setTextureMatrix()). +The snippet below shows adding a buffer with per-instance transformation and +color to a mesh: + +@snippet MagnumShaders.cpp Flat-usage-instancing + +@requires_gl33 Extension @gl_extension{ARB,instanced_arrays} +@requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + @gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays} + in OpenGL ES 2.0. +@requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} in WebGL + 1.0. + @see @ref shaders, @ref Flat2D, @ref Flat3D */ template class MAGNUM_SHADERS_EXPORT Flat: public GL::AbstractShaderProgram { @@ -176,6 +200,37 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab typedef typename Generic::ObjectId ObjectId; #endif + /** + * @brief (Instanced) transformation matrix + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Matrix3 in + * 2D, @ref Magnum::Matrix4 in 3D. Used only if + * @ref Flag::InstancedTransformation is set. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + */ + typedef typename Generic::TransformationMatrix TransformationMatrix; + + /** + * @brief (Instanced) texture offset + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector2. 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 + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + */ + typedef typename Generic::TextureOffset TextureOffset; + enum: UnsignedInt { /** * Color shader output. Present always, expects three- or @@ -263,8 +318,44 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab * WebGL 1.0. * @m_since_latest */ - InstancedObjectId = (1 << 5)|ObjectId + InstancedObjectId = (1 << 5)|ObjectId, #endif + + /** + * Instanced transformation. Retrieves a per-instance + * transformation matrix from the @ref TransformationMatrix + * attribute and uses it together with the matrix coming from + * @ref setTransformationProjectionMatrix() (first the + * per-instance, then the uniform matrix). See + * @ref Shaders-Flat-instancing for more information. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + * @m_since_latest + */ + InstancedTransformation = 1 << 6, + + /** + * Instanced texture offset. Retrieves a per-instance offset vector + * from the @ref TextureOffset attribute and uses it together with + * the matrix coming from @ref setTextureMatrix() (first the + * per-instance vector, then the uniform matrix). Instanced texture + * scaling and rotation is not supported at the moment, you can + * specify that only via the uniform @ref setTextureMatrix(). + * Implicitly enables @ref Flag::TextureTransformation. See + * @ref Shaders-Flat-instancing for more information. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + * @m_since_latest + */ + InstancedTextureOffset = (1 << 7)|TextureTransformation }; /** diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index a2779d889..08bf36814 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -96,11 +96,39 @@ in highp uint instanceObjectId; flat out highp uint interpolatedInstanceObjectId; #endif +#ifdef INSTANCED_TRANSFORMATION +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION) +#endif +#ifdef TWO_DIMENSIONS +in highp mat3 instancedTransformationMatrix; +#elif defined(THREE_DIMENSIONS) +in highp mat4 instancedTransformationMatrix; +#else +#error +#endif +#endif + +#ifdef INSTANCED_TEXTURE_OFFSET +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TEXTURE_OFFSET_ATTRIBUTE_LOCATION) +#endif +in mediump vec2 instancedTextureOffset; +#endif + void main() { #ifdef TWO_DIMENSIONS - gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0); + gl_Position.xywz = vec4(transformationProjectionMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + vec3(position, 1.0), 0.0); #elif defined(THREE_DIMENSIONS) - gl_Position = transformationProjectionMatrix*position; + gl_Position = transformationProjectionMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + position; #else #error #endif @@ -109,7 +137,11 @@ void main() { /* Texture coordinates, if needed */ interpolatedTextureCoordinates = #ifdef TEXTURE_TRANSFORMATION - (textureMatrix*vec3(textureCoordinates, 1.0)).xy + (textureMatrix*vec3( + #ifdef INSTANCED_TEXTURE_OFFSET + instancedTextureOffset + + #endif + textureCoordinates, 1.0)).xy #else textureCoordinates #endif diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 53a1d5a76..688a6c862 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -121,6 +121,8 @@ if(BUILD_GL_TESTS) FlatTestFiles/colored2D.tga FlatTestFiles/colored3D.tga FlatTestFiles/defaults.tga + FlatTestFiles/instanced2D.tga + FlatTestFiles/instanced3D.tga FlatTestFiles/textured2D.tga FlatTestFiles/textured3D.tga FlatTestFiles/textured2D-alpha.tga diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 57971503c..6355f4968 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -35,6 +35,8 @@ #include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" #include "Magnum/DebugTools/CompareImage.h" +#include "Magnum/GL/Context.h" +#include "Magnum/GL/Extensions.h" #include "Magnum/GL/Mesh.h" #include "Magnum/GL/Framebuffer.h" #include "Magnum/GL/Renderer.h" @@ -103,6 +105,9 @@ struct FlatGLTest: GL::OpenGLTester { void renderObjectId3D(); #endif + void renderInstanced2D(); + void renderInstanced3D(); + private: PluginManager::Manager _manager{"nonexistent"}; std::string _testDir; @@ -120,12 +125,12 @@ struct FlatGLTest: GL::OpenGLTester { - Mesa Intel - Mesa AMD - SwiftShader ES2/ES3 - - ARM Mali (Huawei P10) ES2/ES3 - - WebGL 1 / 2 (on Mesa Intel) - - NVidia Windows - - Intel Windows - - AMD on macOS - - iPhone 6 w/ iOS 12.4 + - ARM Mali (Huawei P10) ES2/ES3 (except instancing) + - WebGL 1 / 2 (on Mesa Intel) (except instancing) + - NVidia Windows (except instancing) + - Intel Windows (except instancing) + - AMD on macOS (except instancing) + - iPhone 6 w/ iOS 12.4 (except instancing) */ using namespace Math::Literals; @@ -144,8 +149,10 @@ constexpr struct { #ifndef MAGNUM_TARGET_GLES2 {"object ID", Flat2D::Flag::ObjectId}, {"instanced object ID", Flat2D::Flag::InstancedObjectId}, - {"object ID + alpha mask + textured", Flat2D::Flag::ObjectId|Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured} + {"object ID + alpha mask + textured", Flat2D::Flag::ObjectId|Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured}, #endif + {"instanced transformation", Flat2D::Flag::InstancedTransformation}, + {"instanced texture offset", Flat2D::Flag::Textured|Flat2D::Flag::InstancedTextureOffset} }; const struct { @@ -261,6 +268,11 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderObjectIdTeardown); #endif + addTests({&FlatGLTest::renderInstanced2D, + &FlatGLTest::renderInstanced3D}, + &FlatGLTest::renderSetup, + &FlatGLTest::renderTeardown); + /* Load the plugins directly from the build tree. Otherwise they're either static and already loaded or not present in the build tree */ #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME @@ -1107,6 +1119,175 @@ void FlatGLTest::renderObjectId3D() { } #endif +void FlatGLTest::renderInstanced2D() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() + std::string(" is not supported")); + #elif defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_WEBGL + if(!GL::Context::current().isExtensionSupported() && + !GL::Context::current().isExtensionSupported() && + !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP("GL_{ANGLE,EXT,NV}_instanced_arrays is not supported"); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::instanced_arrays::string() + std::string(" is not supported")); + #endif + #endif + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, + Primitives::Circle2DFlag::TextureCoordinates)); + + /* Three circles, each in a different location */ + struct { + Matrix3 transformation; + Color3 color; + Vector2 textureOffset; + } instanceData[] { + {Matrix3::translation({-1.25f, -1.25f}), 0xff3333_rgbf, + {0.0f, 0.0f}}, + {Matrix3::translation({ 1.25f, -1.25f}), 0x33ff33_rgbf, + {1.0f, 0.0f}}, + {Matrix3::translation({ 0.00f, 1.25f}), 0x9999ff_rgbf, + {0.5f, 1.0f}} + }; + + circle + .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + Flat2D::TransformationMatrix{}, + Flat2D::Color3{}, + Flat2D::TextureOffset{}) + .setInstanceCount(3); + + 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); + + Flat2D{Flat2D::Flag::Textured| + Flat2D::Flag::VertexColor| + Flat2D::Flag::InstancedTransformation| + Flat2D::Flag::InstancedTextureOffset} + .setColor(0xffff99_rgbf) + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})) + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .bindTexture(texture) + .draw(circle); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* Minor differences on AMD, SwiftShader a bit more */ + const Float maxThreshold = 3.0f, meanThreshold = 0.018f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage */ + const Float maxThreshold = 3.0f, meanThreshold = 0.018f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(_testDir, "FlatTestFiles/instanced2D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void FlatGLTest::renderInstanced3D() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() + std::string(" is not supported")); + #elif defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_WEBGL + if(!GL::Context::current().isExtensionSupported() && + !GL::Context::current().isExtensionSupported() && + !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP("GL_{ANGLE,EXT,NV}_instanced_arrays is not supported"); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::instanced_arrays::string() + std::string(" is not supported")); + #endif + #endif + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereFlag::TextureCoordinates)); + + /* Three spheres, each in a different location */ + struct { + Matrix4 transformation; + Color3 color; + Vector2 textureOffset; + } instanceData[] { + {Matrix4::translation({-1.25f, -1.25f, 0.0f}), 0xff3333_rgbf, + {0.0f, 0.0f}}, + {Matrix4::translation({ 1.25f, -1.25f, 0.0f}), 0x33ff33_rgbf, + {1.0f, 0.0f}}, + {Matrix4::translation({ 0.0f, 1.0f, 1.0f}), 0x9999ff_rgbf, + {0.5f, 1.0f}} + }; + + sphere + .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + Flat3D::TransformationMatrix{}, + Flat3D::Color3{}, + Flat3D::TextureOffset{}) + .setInstanceCount(3); + + 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); + + Flat3D{Flat3D::Flag::Textured| + Flat3D::Flag::VertexColor| + Flat3D::Flag::InstancedTransformation| + Flat3D::Flag::InstancedTextureOffset} + .setColor(0xffff99_rgbf) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})) + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .bindTexture(texture) + .draw(sphere); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* Minor differences on AMD, SwiftShader a bit more */ + const Float maxThreshold = 67.67f, meanThreshold = 0.062f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage */ + const Float maxThreshold = 67.67f, meanThreshold = 0.062f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(_testDir, "FlatTestFiles/instanced3D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::FlatGLTest) diff --git a/src/Magnum/Shaders/Test/FlatTest.cpp b/src/Magnum/Shaders/Test/FlatTest.cpp index 39d29ec96..625887002 100644 --- a/src/Magnum/Shaders/Test/FlatTest.cpp +++ b/src/Magnum/Shaders/Test/FlatTest.cpp @@ -39,9 +39,7 @@ struct FlatTest: TestSuite::Tester { void debugFlag(); void debugFlags(); - #ifndef MAGNUM_TARGET_GLES2 - void debugFlagsInstancedObjectId(); - #endif + void debugFlagsSupersets(); }; FlatTest::FlatTest() { @@ -53,10 +51,7 @@ FlatTest::FlatTest() { &FlatTest::debugFlag, &FlatTest::debugFlags, - #ifndef MAGNUM_TARGET_GLES2 - &FlatTest::debugFlagsInstancedObjectId - #endif - }); + &FlatTest::debugFlagsSupersets}); } template void FlatTest::constructNoCreate() { @@ -94,16 +89,23 @@ void FlatTest::debugFlags() { CORRADE_COMPARE(out.str(), "Shaders::Flat::Flag::Textured|Shaders::Flat::Flag::AlphaMask Shaders::Flat::Flags{}\n"); } -#ifndef MAGNUM_TARGET_GLES2 -void FlatTest::debugFlagsInstancedObjectId() { - std::ostringstream out; - +void FlatTest::debugFlagsSupersets() { + #ifndef MAGNUM_TARGET_GLES2 /* InstancedObjectId is a superset of ObjectId so only one should be - *printed */ - Debug{&out} << (Flat3D::Flag::ObjectId|Flat3D::Flag::InstancedObjectId); - CORRADE_COMPARE(out.str(), "Shaders::Flat::Flag::InstancedObjectId\n"); + printed */ + { + std::ostringstream out; + Debug{&out} << (Flat3D::Flag::ObjectId|Flat3D::Flag::InstancedObjectId); + CORRADE_COMPARE(out.str(), "Shaders::Flat::Flag::InstancedObjectId\n"); + } + #endif + + /* InstancedTextureOffset is a superset of TextureTransformation so only + one should be printed */ + std::ostringstream out; + Debug{&out} << (Flat3D::Flag::InstancedTextureOffset|Flat3D::Flag::TextureTransformation); + CORRADE_COMPARE(out.str(), "Shaders::Flat::Flag::InstancedTextureOffset\n"); } -#endif }}}} diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/instanced2D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/instanced2D.tga new file mode 100644 index 000000000..a130de40e Binary files /dev/null and b/src/Magnum/Shaders/Test/FlatTestFiles/instanced2D.tga differ diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/instanced3D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/instanced3D.tga new file mode 100644 index 000000000..c1c4d9640 Binary files /dev/null and b/src/Magnum/Shaders/Test/FlatTestFiles/instanced3D.tga differ